Baixe o app para aproveitar ainda mais
Prévia do material em texto
Conceitos de threads Apresentação Os computadores são ferramentas cada vez mais úteis no dia a dia das pessoas, permitindo a execução de diversas tarefas ao mesmo tempo. Por exemplo, você pode ver um vídeo e realizar uma busca por um livro simultaneamente. Essa capacidade de executar tarefas de modo simultâneo é possível pela utilização de threads. As threads, também conhecidas como processos “leves”, permitem que múltiplas tarefas colaborem entre si, simultaneamente. Desse modo, é de grande importância entender o impacto das threads na otimização dos sistemas operacionais. Nesta Unidade de Aprendizagem, você aprenderá o conceito de thread, a sua estruturação e quais estratégias para a implementação de threads são usadas nos sistemas operacionais. Bons estudos. Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados: Conceituar thread.• Descrever o modelo de thread clássico.• Discutir implementações de threads.• Desafio As threads mudaram o funcionamento de diversos programas, permitindo que tarefas independentes possam colaborar entre si, de modo a otimizar a execução das tarefas e facilitar a maneira como cada usuário interage com os programas. A implementação de alguns programas pode não estar utilizando threads, assim, tanto a execução pode ser mais lenta quanto o uso dos recursos pode ser menos eficiente. Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar. https://statics-marketplace.plataforma.grupoa.education/sagah/d9e0ce29-ab22-4ac8-ab3f-9703fc04cb61/19a7f0b2-a807-4a39-8264-ff1c49732991.png Após observar o gráfico, responda as seguintes questões: a) O que você pode concluir quanto ao desempenho da aplicação? Estava realmente travando como foi relatado pelo cliente? b) Qual solução poderia ser empregada? Como seria a implementação da solução na aplicação? Infográfico A implementação de threads pelos sistemas operacionais possibilitou que os usuários executassem mais de uma tarefa ao mesmo tempo. Contudo, o suporte ao uso de threads não existia desde o começo. No início, os computadores monoprogramados somente ofereciam a possibilidade de execução de um única tarefa por vez. Com o surgimento da necessidade da execução de mais tarefas de modo simultâneo foram desenvolvidas diferentes estratégias de implementação de threads, as quais deram origem à multiprogramação. Neste Infográfico, você vai conhecer as principais estratégias de implementação de threads. Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar. https://statics-marketplace.plataforma.grupoa.education/sagah/0e63978d-b590-4aa1-b5b5-51602f9702ef/e9427450-b315-4c83-8c25-b9d211f0bdba.png Conteúdo do livro Na era atual da computação, em que o desempenho e a eficiência são cruciais, a compreensão do conceito de threads torna-se indispensável para qualquer profissional da área de tecnologia. As threads, componentes fundamentais da programação paralela e concorrente, oferecem uma maneira de dividir o processo de execução em subproblemas menores que podem ser resolvidos simultaneamente, melhorando de forma significativa o desempenho do aplicativo. No capítulo Conceitos de threads, base teórica desta Unidade de Aprendizagem, vamos explorar os conceitos fundamentais das threads, o modelo clássico de thread e três implementações amplamente utilizadas: POSIX Threads (Pthreads), OpenMP e Java Threads. Poderemos compreender o papel das threads na programação paralela e concorrente, sua relação com os processos, bem como os benefícios que elas trazem para o desempenho dos aplicativos. Como veremos, o modelo clássico de thread, conhecido como "modelo 1:1", mapeia cada thread de usuário para uma thread de kernel, permitindo a execução simultânea de tarefas em um programa. Neste capítulo vamos, também, analisar as implementações de threads por meio da Pthreads, da OpenMP e da Java Threads, destacando suas características e diferenças. Ainda, mostraremos exemplos de código utilizando essas implementações para resolver o problema de cálculo da soma de elementos de um vetor. Ao final do capítulo, você terá adquirido uma compreensão abrangente das threads, do modelo clássico de thread e das implementações Pthreads, OpenMP e Java Threads, tornando-se capaz de desenvolver programas concorrentes eficientes e aproveitar ao máximo o potencial das threads na melhoria do desempenho e da escalabilidade dos aplicativos. Boa leitura. SISTEMAS OPERACIONAIS OBJETIVOS DE APRENDIZAGEM > Conceituar thread. > Descrever o modelo de thread clássico. > Discutir implementações de threads. Introdução Em um mundo onde a computação evolui rapidamente e a demanda por sistemas eficientes e de alto desempenho cresce constantemente, é essencial entender os mecanismos que possibilitam a execução paralela de tarefas. As threads são um dos principais componentes para se alcançar a concorrência e o paralelismo em sistemas computacionais. Elas são responsáveis por dividir um programa em múltiplas unidades de execução independentes, permitindo que diferentes partes do código sejam executadas simultaneamente, o que aumenta o desempenho global da aplicação. Neste capítulo, você poderá compreender o que são threads e como elas se relacionam com processos, refletindo sobre os benefícios e os desafios oferecidos pelo uso de threads na programação paralela e concorrente. Além disso, você vai conhecer o modelo de threads POSIX, que é amplamente utilizado em sistemas operacionais Unix e compatíveis, identificando sua estrutura, seu funcionamento e o modo como ele se relaciona com o conceito de threads no espaço do usuário. Também vai conhecer as diferentes abordagens para implementar threads, incluindo threads no espaço do usuário, threads no núcleo e implementações híbridas, explorando como essas implementações podem ser aplicadas em am- bientes de computação de alto desempenho. Assim, você poderá compreender de forma abrangente os conceitos de threads e suas aplicações práticas em sistemas Conceitos de threads Matheus da Silva Serpa computacionais, tornando-se capaz de identificar as melhores abordagens e implementações para alcançar o máximo de desempenho e eficiência em seus projetos de ciência da computação e desenvolvimento de software. Desvendando o universo das threads Quando falamos sobre computação paralela e concorrente, um dos conceitos mais importantes a ser entendido é o de threads. Em termos leigos, uma thread, ou linha de execução, pode ser entendida como uma sequência de instruções de um programa que pode ser executada independentemente das demais. No entanto, essa definição simplista não faz jus à complexidade e à importância das threads para a computação moderna. Por isso, nesta seção, vamos nos aprofundar no conceito de threads, explorando seu funcionamento, seus benefícios e as implicações de seu uso (TANENBAUM; BOS, 2015). Uma thread é, essencialmente, uma subdivisão de um processo. Ao passo que um processo é uma instância de um programa em execução que tem seu próprio espaço de endereço e recursos do sistema, uma thread é uma sequência de instruções dentro desse processo que pode ser programada para executar tarefas em paralelo com outras threads. Cada thread compartilha os recursos do processo-pai, incluindo o espaço de memória e os arquivos abertos, mas mantém seu próprio contador de programa, que mantém o controle de qual instrução a thread está atualmente executando, e sua própria pilha, que contém as informações necessárias para a execução da thread (STALLINGS, 2018). A capacidade das threads de compartilhar recursos do processo-pai, mas executar de forma independente, é o que possibilita a programação paralela e concorrente. Quando múltiplas threads são usadas, diferentes partes de um programa podem ser executadas de forma simultânea, potencialmente melhorando o desempenho e a eficiência do programa. Além disso, o uso dethreads pode tornar um programa mais responsivo, permitindo que ele continue a responder às ações do usuário, mesmo quando parte do programa está ocupada com uma tarefa intensiva em termos de recursos. Para ilustrar isso, vejamos um exemplo. Em um processador de texto, poderíamos ter uma thread que lida com a entrada do usuário (p. ex., a di- gitação de texto), ao passo que outra thread é responsável por salvar o documento atual em segundo plano. Se essas duas tarefas fossem realizadas sequencialmente no mesmo processo, a aplicação poderia ficar travada en- quanto o documento estivesse sendo salvo, impedindo o usuário de continuar Conceitos de threads2 a digitar. No entanto, com a implementação de threads, essas tarefas podem ocorrer simultaneamente, melhorando a experiência do usuário. A Figura 1 apresenta um diagrama que compara dois modelos de execução de processos: um processo único e um processo multithread. Esse diagrama facilita a compreensão da diferença fundamental entre esses dois modelos e da maneira como as threads operam dentro de um processo. Figura 1. (a) Processo com uma única thread. (b) Processo com várias threads. Fonte: Adaptada de Bell (2013). Código Código Pilha Pilha Pilha Regs. Regs. Regs. Contador Contador Contador Estado Estado Estado Dados Arquivos Pilha Registradores Thread Thread Thread Thread Dados Arquivos A B Na Figura 1a, é representado um processo único. Nesse modelo, há apenas uma sequência de execução, indicada pela única linha vertical. Isso significa que todas as tarefas executadas por esse processo ocorrem uma após a outra em uma única sequência de instruções. Por exemplo, se esse processo fosse um programa de edição de texto, ele só poderia executar uma tarefa de cada vez — seja receber a entrada do usuário, verificar a ortografia ou salvar o documento. Já na Figura 1b, vemos a representação de um processo multi- thread. Esse processo contém várias threads (linhas verticais), que operam dentro do espaço de endereço do processo. Cada thread é uma sequência separada de execução e pode operar independentemente das outras. Isso significa que várias tarefas podem ser executadas simultaneamente dentro do mesmo processo. Retornando ao exemplo do programa de edição de texto, isso poderia permitir que uma thread recebesse a entrada do usuário, enquanto outra thread estivesse verificando a ortografia e uma terceira thread Conceitos de threads 3 estivesse salvando o documento — tudo simultaneamente. A Figura 1 também destaca que todas as threads dentro de um processo compartilham o mesmo espaço de endereço. Ou seja, elas podem acessar e modificar as mesmas variáveis e estruturas de dados, o que facilita a comunicação entre threads. No entanto, também pode levar a condições de corrida, que são situações em que o resultado de um programa depende da ordem específica em que as threads são programadas. Ainda, a Figura 1 destaca a eficiência potencial dos programas multithread — ao dividir as tarefas de um programa entre várias threads, é possível fazer um uso mais eficiente dos recursos do sistema e melhorar o desempenho do programa —, mas também indica as possíveis complexidades associadas à programação multithread, como a necessidade de sincronizar as threads para evitar condições de corrida. Agora, é importante destacar que existem dois tipos principais de threads: as threads do espaço do usuário e as threads do kernel. As threads do espaço do usuário são totalmente gerenciadas pelo programa do usuário, e o ker- nel (a parte central do sistema operacional) não tem conhecimento de sua existência. Isso significa que o tempo e os recursos necessários para criar, destruir e alternar entre threads do espaço do usuário são mínimos, já que essas operações são realizadas sem a necessidade de interação com o kernel. No entanto, isso também implica algumas limitações, como o fato de que, se uma única thread do espaço do usuário for bloqueada (p. ex., se estiver esperando por um recurso), todas as outras threads do mesmo processo também serão bloqueadas, já que o kernel não pode gerenciar essas threads independentemente (TANENBAUM; BOS, 2015). As threads do kernel são suportadas e gerenciadas diretamente pelo sistema operacional. Assim, cada thread do kernel é tratada pelo sistema operacional como um processo separado, com seu próprio espaço de ende- reçamento e estado de execução. Isso permite uma maior flexibilidade, pois o bloqueio de uma thread do kernel não afeta as outras threads do mesmo processo, e uma maior integração com o sistema operacional, já que cada thread do kernel pode usar todas as facilidades do sistema operacional. No entanto, isso também torna a criação, a destruição e a troca de contexto entre threads do kernel mais custosas em termos de desempenho, uma vez que essas operações exigem interação com o kernel (STALLINGS, 2018). Nesta seção, você pôde compreender o que são threads, como elas funcio- nam e suas implicações. Nas próximas seções, continuaremos nossa explo- ração das threads, identificando como elas são implementadas e utilizadas em diversos contextos de programação. Conceitos de threads4 O paradigma do modelo clássico de threads Uma vez compreendido o conceito básico de threads, é hora de nos aprofun- darmos no modelo clássico de thread. Esse modelo desempenha um papel crucial na programação concorrente e paralela, formando a base para muitos sistemas operacionais e aplicações multithread modernas (TANENBAUM; BOS, 2015). O modelo clássico de thread, também conhecido como “modelo 1:1”, é um modelo em que cada thread de usuário é mapeada para uma thread de kernel. Esse modelo é amplamente adotado por muitos sistemas operacionais modernos, como o Linux e o Windows, devido à sua simplicidade e eficiência. A principal vantagem desse modelo é que ele permite que o sistema opera- cional gerencie todas as threads diretamente, facilitando o escalonamento de threads e o gerenciamento de recursos (STALLINGS, 2018). No modelo 1:1, cada thread tem seu próprio contexto de execução no kernel, incluindo um contador de programa, um conjunto de registradores e uma pilha de kernel. Isso permite que o sistema operacional interrompa e retome threads individualmente, sem afetar outras threads no mesmo processo. Além disso, como cada thread de usuário tem uma correspondente no kernel, o bloqueio de uma thread (p. ex., para aguardar a entrada/saída) não impede que outras threads no mesmo processo continuem a execução (TANENBAUM; BOS, 2015). Vale ressaltar que, apesar de suas vantagens, o modelo 1:1 também apre- senta desvantagens. A principal é o custo de desempenho associado ao gerenciamento de threads no nível do kernel. Como cada thread é tratada como um processo separado pelo sistema operacional, as operações de criação, destruição e troca de contexto das threads podem ser mais custosas em termos de tempo e recursos do sistema. Além disso, como o número de threads que podem ser criadas é limitado pelos recursos do sistema opera- cional, esse modelo pode não ser adequado para programas que requerem um grande número de threads (STALLINGS, 2018). A Figura 2 ilustra o modelo 1:1 de mapeamento de threads de usuário para threads de núcleo. Nesse modelo, cada thread de usuário é mapeada diretamente para uma thread de núcleo correspondente. Tal mapeamento elimina a necessidade de implementar bibliotecas específicas para as threads de usuário. Além disso, uma vantagem desse modelo é que, se uma thread de núcleo de um processo for suspensa, as outras threads de núcleo não serão afetadas. Esse modelo também permite a execução simultânea de várias threads da mesma aplicação, caso o hardware do sistema tenha mais Conceitos de threads 5 de um processador. Embora o modelo 1:1 seja mais simples de entender e implementar do que outros modelos, ele ainda traz desafios significativos em termos de gerenciamento de recursos e escalonamento de threads. Figura 2. Modelo1:1 de thread. Fonte: Adaptada de Silva Junior e Primor (2017). Memória Processo pa Processo pb Núcleo Threads de núcleo Threads Threads Memória É importante notar que o modelo de thread clássico não é a única opção disponível para a implementação de threads. Além dele, existem outros modelos de implementação de threads: o modelo N:1 e o modelo N:M. No modelo N:1, as threads de usuário são gerenciadas por uma biblioteca e são executadas em uma única thread de núcleo. Embora seja leve e fácil de imple- mentar, esse modelo apresenta limitações nas operações de entrada/saída e compartilhamento de recursos. Já no modelo N:M, as threads de usuário são mapeadas em um número variável de threads de núcleo. Esse modelo oferece flexibilidade e permite ajustes, dependendo das necessidades da aplicação. No entanto, pode ser mais complexo de implementar e requer um gerenciamento cuidadoso dos recursos do sistema. Cada modelo oferece vantagens e desvantagens, e a escolha adequada depende das características da aplicação e do sistema operacional em ques- Conceitos de threads6 tão. É importante considerar fatores como escalabilidade, desempenho e gerenciamento de recursos ao selecionar o modelo de threads mais apropriado. As startups de tecnologia muitas vezes enfrentam desafios únicos em termos de escalabilidade. À medida que a base de usuários de um aplicativo ou serviço cresce, também cresce a demanda por recursos do sistema. O uso eficiente de threads pode ser a chave para lidar com essa demanda crescente. Utilizando o modelo clássico de threads, é possível criar aplicações altamente escaláveis que podem suportar um número crescente de usuários, sem a necessidade de investimentos proporcionais em hardware (DOWNEY, 2016). Um exemplo disso é a startup de tecnologia XYZ (nome fictício), que oferece um serviço de streaming de vídeo. Quando a startup começou, sua base de usuários era relativamente pequena, e um modelo de processo único era sufi- ciente para atender à demanda. No entanto, conforme foi crescendo e atraindo mais usuários, a empresa começou a enfrentar problemas de escalabilidade. O serviço tornou-se lento durante os períodos de pico de uso, e os usuários começaram a reclamar de atrasos e interrupções no streaming (DOWNEY, 2016). Para resolver esse problema, a startup XYZ decidiu reescrever seu serviço de back-end usando o modelo clássico de threads. Cada solicitação de streaming de um usuário agora é tratada por uma thread separada, permitindo que várias solicitações sejam processadas simultaneamente. Além disso, a startup imple- mentou um pool de threads, que permite reutilizar threads após a conclusão de uma solicitação, em vez de criar uma nova thread para cada solicitação, melhorando ainda mais a eficiência e o desempenho (DOWNEY, 2016). O resultado foi um aumento significativo no desempenho e na escalabilidade do serviço de streaming. Assim, a startup XYZ foi capaz de atender um número muito maior de usuários simultaneamente, sem a necessidade de investir em hardware adicional. Além disso, a qualidade do streaming melhorou, com me- nos atrasos e interrupções, levando a uma maior satisfação do usuário e ao crescimento contínuo da startup (DOWNEY, 2016). Esse estudo de caso ilustra a relevância do modelo clássico de threads para startups de tecnologia. Ao permitir a execução simultânea de várias tarefas e o uso eficiente dos recursos do sistema, o modelo clássico de threads pode ser uma ferramenta poderosa para atingir a escalabilidade e a eficiência necessárias em um ambiente de startup. O modelo clássico de thread 1:1 tem uma presença relevante na progra- mação concorrente e paralela, sendo essencial para o funcionamento de muitos sistemas operacionais modernos e aplicações multithread. Ele se destaca pela eficiência e simplicidade, apesar dos desafios existentes em termos de gerenciamento de recursos e escalonamento de threads. Agora que compreendemos as características fundamentais e a importância do modelo clássico de thread, podemos avançar para explorar algumas implementações de threads disponíveis atualmente, a fim de entendermos suas particularida- Conceitos de threads 7 des e como podem ser aplicadas de forma eficaz. A seguir, vamos conhecer três implementações amplamente utilizadas no mundo da programação: POSIX Threads, OpenMP e Java Threads. Explorando as implementações de threads: POSIX Threads, OpenMP e Java Threads Threads são a base da programação concorrente, permitindo que várias partes de um programa sejam executadas simultaneamente. Existem várias maneiras de implementar threads. Neste capítulo, vamos nos concentrar em três implementações amplamente utilizadas: POSIX Threads, OpenMP e Java Threads. POSIX Threads A POSIX Threads (ou Pthreads) é uma biblioteca padrão amplamente adotada para a criação e sincronização de threads. Essa biblioteca, uma especificação estabelecida pelo Institute of Electrical and Electronics Engineers (IEEE), é independente de linguagem e tem uma ampla compatibilidade com diver- sas plataformas. Em termos de linguagens de programação, a Pthreads é comumente utilizada em C e C++, que oferecem suporte nativo à biblioteca. No que diz respeito aos sistemas operacionais, a biblioteca Pthreads é pre- dominantemente implementada em ambientes baseados em Unix, incluindo sistemas operacionais Linux. Embora ela não seja nativamente suportada pelo Windows, existem bibliotecas de terceiros que permitem o uso de Pthreads nesse sistema operacional, o que evidencia sua versatilidade e seu amplo alcance (BUTENHOF, 1997). Em Pthreads, cada thread é um objeto independente com seu próprio contexto de execução, incluindo registros de CPU e espaço de endereço. As threads compartilham o mesmo espaço de endereço do processo-pai, facilitando a comunicação entre threads por meio do compartilhamento de variáveis. No entanto, essa característica também pode levar a problemas complexos de sincronização, como condições de corrida. A seguir, é apresentado um exemplo de código em linguagem C, o qual utiliza POSIX Threads para calcular a soma dos elementos de um vetor. Esse exemplo contém apenas a função específica que realiza esse cálculo. Vale ressaltar que esse trecho de código não contém a função main, o ponto Conceitos de threads8 de entrada típico de um programa, e, portanto, deve ser integrado em um programa maior para ser executado corretamente. A função thread _ sum (linha 1) é executada por cada thread, recebendo um ID como argumento (linha 2) e definindo o início (linha 3) e o fim (linha 4) do segmento do vetor que a thread processará. Cada thread calcula a soma dos elementos em seu segmento (linhas 5 a 9) e, então, utilizando um mutex para garantir a exclusão mútua, adiciona essa soma a uma variável global (linhas 11 a 13). A thread termina sua execução na linha 15. A soma final dos elementos do vetor será armazenada na variável global após a conclusão de todas as threads. 1. void* thread _ sum(void* arg) { 2. int thread _ id = *(int*)arg; 3. int start = thread _ id * (ARRAY _ SIZE / NUM _ THREADS); 4. int end = start + (ARRAY _ SIZE / NUM _ THREADS); 5. int local _ sum = 0; 6. 7. for (int i = start; i < end; i++) { 8. local _ sum += array[i]; 9. } 10. 11. pthread _ mutex _ lock(&mutex); 12. sum += local _ sum; 13. pthread _ mutex _ unlock(&mutex); 14. 15. pthread _ exit(NULL); 16.} Open MP A OpenMP (Open Multi-Processing) é uma API (Application Programming In- terface) que suporta programação paralela de memória compartilhada em C, C++ e Fortran em várias plataformas. A OpenMP é amplamente utilizada para computação de alto desempenho devido à sua facilidade de uso e eficiência (CHAPMAN; JOST; VAN DER PAS, 2008). Diferentemente da Pthreads, que requer que o programador gerencie explicitamente a criação, a sincronização e a destruição de threads, a OpenMP abstrai muitos desses detalhes,permitindo que os programadores se concen- trem no paralelismo do problema. As diretivas do compilador são usadas para Conceitos de threads 9 especificar regiões de código a serem executadas em paralelo, e o sistema de tempo de execução da OpenMP gerencia a criação e a sincronização de threads. O trecho de código apresentado a seguir é um exemplo de como utilizar a biblioteca OpenMP em C para calcular a soma dos elementos de um vetor. Ele apresenta uma única função, chamada thread _ sum. É importante notar que, para esse exemplo, não foi incluído um método main, que geralmente seria o ponto de entrada para a execução do programa. A função thread _ sum poderia ser integrada a um programa maior, sendo chamada a partir do método main ou de outra parte do código. A função thread _ sum (linha 1) inicializa a variável sum com zero (linha 2). O laço de repetição (linha 5) é executado em paralelo entre várias threads, devido à diretiva #pragma omp parallel for reduction(+:sum) (linha 4). Isso significa que o vetor é dividido e que cada pedaço é processado por uma thread diferente, somando os elementos do vetor à variável sum (linha 6). Ao final do laço, graças à cláusula de redução, a variável sum contém a soma de todas as somas parciais calculadas pelas threads. A soma total é, então, retornada na linha 9. 1. int thread _ sum() { 2. int sum = 0; 3. 4. #pragma omp parallel for reduction(+:sum) 5. for (int i = 0; i < ARRAY _ SIZE; i++) { 6. sum += array[i]; 7. } 8. 9. return sum; 10.} Java Threads A Thread Class em Java, presente no pacote java.lang, representa uma thread de execução no contexto da linguagem Java, e é essencial para a execução de tarefas em paralelo (SILBERSCHATZ; GALVIN; GAGNE, 2013). A classe Thread contém uma série de métodos que permitem o controle e o gerenciamento de threads. O entendimento dessas operações é crucial para a programação concorrente e paralela e, por isso, é um elemento importante na formação de estudantes de computação. Em particular, o método yield() tem um comportamento distinto. Quando uma thread em execução chama o método yield(), a thread voluntariamente Conceitos de threads10 cede seu tempo de processador, permitindo que outras threads sejam exe- cutadas. Segundo a documentação oficial da Oracle, o estado da thread é alterado de “em execução” para “pronto” (ORACLE, 2023). Portanto, a thread que chamou o método yield() não é bloqueada nem termina; ela sim- plesmente se coloca no final da fila de threads prontas para execução, na esperança de que outras threads possam utilizar o tempo de processador. No entanto, é importante ressaltar que o comportamento exato do método yield() pode variar entre diferentes sistemas operacionais e versões de JVM (Java Virtual Machine). O trecho de código mostrado a seguir, escrito em Java, representa uma classe ThreadSum que implementa a interface Runnable. A classe contém uma única função: o método run(). Cabe destacar que, para o propósito desse exemplo, o método main() não está incluso. Portanto, o código aqui apresentado é uma parte de um programa maior, em que a classe ThreadSum pode ser instanciada e seus métodos podem ser chamados a partir de um método main() ou de qualquer outro ponto no código. No código usado como exemplo, o objetivo é realizar a soma de um seg- mento específico de um array de inteiros, dividido entre as threads. A classe tem um construtor para definir o ID da thread, os limites do segmento do array e o próprio array. No método run(), a soma local é calculada, e, em seguida, a soma total é atualizada de forma sincronizada. O método getSum() retorna o valor final da soma total. 1. public class ThreadSum implements Runnable { 2. private int threadId; 3. private int start; 4. private int end; 5. private static int[] array; 6. private static int sum = 0; 7. private static Object lock = new Object(); 8. 9. public ThreadSum(int threadId, int[] array) { 10. this.threadId = threadId; 11. this.array = array; 12. int arraySize = array.length; 13. this.start = threadId * (arraySize / Main. NUM _ THREADS); 14. this.end = start + (arraySize / Main.NUM _ THREADS); Conceitos de threads 11 15. } 16. 17. @Override 18. public void run() { 19. int localSum = 0; 20. 21. for (int i = start; i < end; i++) { 22. localSum += array[i]; 23. } 24. 25. synchronized (lock) { 26. sum += localSum; 27. } 28. } 29. 30. public static int getSum() { 31. return sum; 32. } 33.} A escolha entre Java Threads, Pthreads e OpenMP depende de vários fa- tores, incluindo a natureza do problema, a experiência do programador e as características do sistema de destino. A Java Threads e a Pthreads oferecem mais controle, mas podem ser mais complexas de usar. Já a OpenMP oferece maior abstração, o que a torna mais fácil de usar, mas possivelmente menos flexível. A Java Threads proporciona uma interface orientada a objeto para a programação multithread e é integrada à linguagem Java, o que a tornando uma opção natural para a programação multithread em Java. No entanto, a escolha entre esses três depende em grande parte do contexto específico e das necessidades do programa em questão. Neste capítulo, exploramos os conceitos fundamentais de threads, in- cluindo as threads da linguagem de programação Java, e discutimos o modelo clássico de thread, conhecido como “modelo 1:1”. Além disso, analisamos três implementações amplamente utilizadas: a POSIX Threads (Pthreads), a OpenMP e a Java Threads. Assim, você pôde compreender a importância das threads na programação paralela, bem como o funcionamento do modelo clássico de thread e as características das implementações POSIX Threads, OpenMP e Java Threads, o que permite a criação de programas concorrentes eficientes. Essa compreensão abrangente facilitará o uso efetivo desses recursos para melhorar o desempenho e a escalabilidade de suas aplicações. Conceitos de threads12 Referências BELL, J. Operating systems course notes. [S. l.: s. n.], 2013. Disponível em: https://www. cs.uic.edu/~jbell/CourseNotes/OperatingSystems/. Acesso em: 26 maio 2023. BUTENHOF, D. R. Programming with POSIX threads. [S. l.]: Addison-Wesley Professional, 1997. CHAPMAN, B.; JOST, G.; VAN DER PAS, R. Using OpenMP: portable shared memory parallel programming. Cambridge: MIT, 2008. DOWNEY, A. B. The little book of semaphores. 2nd. ed. [S. l.]: Green Tea Press, 2016. ORACLE. Class thread. [S. l.]: Java™ Platform Standard, 2023. Disponível em: https://docs. oracle.com/javase/8/docs/api/java/lang/Thread.html#yield. Acesso em: 26 maio 2023. SILBERSCHATZ, A.; GALVIN, P. B.; GAGNE, G. Sistemas operacionais. 9. ed. Rio de Janeiro: LTC, 2013. SILVA JUNIOR, M.; PRIMOR, R. L. Threads: o que são? [Ponta Grossa: UEPG, 2017]. Dis- ponível em: https://deinfo.uepg.br/~alunoso/2017/threads/. Acesso em: 26 maio 2023. STALLINGS, W. Operating systems: internals and design principles. 9th. ed. London: Pearson, 2018. TANENBAUM, A. S.; BOS, H. Modern operating systems. 4th. ed. London: Pearson, 2015. Os links para sites da web fornecidos neste capítulo foram todos testados, e seu funcionamento foi comprovado no momento da publicação do material. No entanto, a rede é extremamente dinâmica; suas páginas estão constantemente mudando de local e conteúdo. Assim, os editores declaram não ter qualquer responsabilidade sobre qualidade, precisão ou integralidade das informações referidas em tais links. Conceitos de threads 13 Dica do professor A utilização de threads nos sistemas operacionais e nos programas de usuário proporciona diversos benefícios. Por exemplo, você pode escrever um texto usando uma thread com monitoria das teclas pressionadas no teclado, enquanto outra analisa o texto completo e faz a análiseortográfica para você, otimizando seu trabalho. Acompanhe o vídeo a seguir, e você vai aprender de um jeito diferente sobre threads. Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar. https://fast.player.liquidplatform.com/pApiv2/embed/cee29914fad5b594d8f5918df1e801fd/b4e173454b59d937261758335509ff28 Exercícios 1) As threads podem ter seu estado de execução alterado entre pronto, bloqueado, em execução e finalizado, em decorrência de diversos eventos. Considerando que uma thread foi implementada utilizando a linguagem de programação Java, quando uma thread chama o método yield(), qual transição de estado acontece? A) O estado passa de “bloqueado” para “em execução”. B) O estado passa de “em execução” para “bloqueado”. C) O estado passa de “em execução” para “finalizado”. D) O estado passa de “em execução” para “pronto”. E) O estado passa de “pronto” para “em execução”. 2) Uma ferramenta de edição de texto provê algumas funcionalidades para auxiliar no trabalho dos seus usuários. Após a inicialização da ferramenta, esta inicia a: 1. ler a entrada do teclado e representar o texto em tela; 2. verificar a ortografia do texto conforme um conjunto de normas; 3. verificar e atualizar a estilização do texto; 4. executar salvamentos periódicos como backup. Considerando que cada funcionalidade descrita acima é executa por uma thread exclusiva, quantas threads são necessárias para a execução descrita da ferramenta? A) 4 threads. B) 3 threads. C) 5 threads. D) 6 threads. E) 2 threads. 3) João e Matheus estão desenvolvendo uma ferramenta para um trabalho de faculdade. Ambos concordam que o desempenho é um fator muito importante e, para isso, pretendem adotar alguma estratégia que permita executar tarefas simultaneamente. Existem duas propostas atualmente: 1. Implementar a ferramenta utilizando três processos separados com uma thread cada. 2. Implementar a ferramenta utilizando um processo com três threads. A dupla está dividida nas opiniões, Matheus é favorável à segunda proposta, contudo, João ainda não concorda. Qual argumento Matheus poderia apresentar para tornar a segunda proposta vencedora? A) A aplicação depende de muita utilização da unidade de processamento. B) Para a aplicação ter um melhor desempenho, as threads precisam disputar ferozmente os recursos. C) A aplicação tem muitas operações de entrada/saída de dados. D) As threads não compartilham os recursos entre si. E) As threads precisam ser totalmente independentes. 4) A implementação de threads no espaço do usuário é uma das estratégias de implementação existentes para utilização de threads em sistemas operacionais. Como qualquer estratégia, pode ter pontos positivos e negativos. Assinale uma vantagem dessa estratégia. A) Quando uma thread executa uma chamada de sistema, as demais threads do mesmo processo não são bloqueadas. B) A tabela de threads fica centralizada no núcleo do sistema operacional. C) O núcleo do sistema tem acesso às threads dentro dos processos, podendo escaloná-las como desejado. D) Permite a implementação de threads no espaço do usuário (de modo interno ao processo), mesmo em sistemas monoprogramados cujo núcleo não tem suporte a threads. E) Quando uma thread para de ser executada (sem desistência voluntária), a próxima thread a ser executada sempre é do mesmo processo. 5) Uma das principais vantagens do uso threads em relação aos processos consiste na possibilidade de compartilhamento de recursos entre diferentes linhas de execução. Contudo, as threads devem armazenar informações que sejam somente suas. Assinale a alternativa na qual as informações listadas somente pertençam às threads. A) Contador de programa e estado de execução. B) Contador de programa e espaço de endereçamento. C) Espaço de endereçamento e arquivos abertos. D) Processos filhos e registradores. E) Arquivos abertos e pilha. Na prática A utilização de threads também permite uma melhor utilização dos recursos computacionais. Em muitos casos, se uma thread está esperando a leitura de um arquivo, não há motivo para que ela também detenha o uso da unidade de processamento, pois a unidade ficará ociosa. Desse modo, o mais adequado é que o controle seja passado para outra thread realizar seu trabalho, otimizando o uso da unidade de processamento. Nesta Na Prática, você vai conhecer um exemplo de aplicação de threads na otimização de programas. Conteúdo interativo disponível na plataforma de ensino! Saiba + Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do professor: Sistemas operacionais Nesta videoaula, você vai rever os conceitos vistos nesta Unidade de Aprendizagem, além de alguns exemplos de implementação de threads usando linguagem de programação. Confira. Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar. O que são e como funcionam as threads Neste artigo, você vai ver diversos detalhes de como as threads são implementadas pelos sistemas operacionais por meio de diversos exemplos, usando a linguagem de programação C++. Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar. Programação concorrente e threads Neste site, você vai aprender a implementar threads de modo programático, utilizando a linguagem de programação Java. Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar. https://www.youtube.com/embed/Tbwu55Iov5s https://blog.pantuza.com/artigos/o-que-sao-e-como-funcionam-as-threads https://www.caelum.com.br/apostila-java-orientacao-objetos/apendice-programacao-concorrente-e-threads/#threads
Compartilhar