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