Threads POSIX

threads

Olá, caro leitor. Este artigo tem como objetivo apresentar as funções básicas da biblioteca pthread. Assim, uma breve introdução sobre processos e threads é apresentada antes de apontar possíveis aplicações.

Sobre Processos e Threads

Um dos conceitos mais importantes relacionados a sistemas operacionais é denominado processo. De modo geral, um processo é uma abstração de um programa em execução. Tal programa possui um espaço de endereçamento e, em sistemas tradicionais, apenas um thread (segmento ou fluxo de controle) de execução. Além disso, o processo contém toda informação relacionada ao seu contexto de execução, por exemplo, o contador de programa, apontador de pilha e demais registradores. Dito de outra maneira, é um programa com com sua função principal, denominada main, sendo executado sequencialmente, instrução por instrução.

No entanto, em alguns sistemas é possível criar mais de um thread no mesmo processo, isto é, no mesmo espaço endereçamento. Nesse caso, mais de um fluxo de execução ocorre dentro do mesmo processo! Diante disso, é importante destacar algumas aplicações:

  • Tratar atividades que ocorrem “simultaneamente”;
  • Dividir a aplicação em tarefas que acessam recursos compartilhados;
  • Reduzir o tamanho de uma aplicação, uma vez que threads ocupam menos espaço em relação aos processos;
  • São mais fáceis de criar e destruir;
  • A sobreposição de tarefas pode acelerar a aplicação;
  • Possibilitam paralelismo real em sistemas multicore.

Principais diferenças

É importante destacar que threads e processos são conceitos diferentes. Como dito anteriormente, o processo é basicamente um agrupador de recursos (código e dados) e possui uma identidade, enquanto as threads são criadas no contexto de um processo e compartilham o mesmo espaço de endereçamento. À vista disso, threads não são independentes como os processos. Pois, embora compartilhem o mesmo espaço de endereçamento dentro de um processo, cada thread possui os mecanismos para gerenciar seu contexto de execução. Assim, threads possuem seu próprio contador de programa, apontador de pilha e registradores.

Contexto de threads

Os threads criados ocupam a CPU do mesmo modo que o processo criador, e também são escalonadas pelo próprio processo. Nesse contexto, quando uma aplicação multithread é executada, esses threads podem estar em qualquer um destes estados: em execução, bloqueado (aguardando), pronto para ser executado ou concluído (finalizado). Isso é ilustrado na Figura 1.

Estados de operação de uma thread.
Figura 1: Estados de operação de uma thread.

Portabilidade de Aplicações

Para padronizar a utilização de threads em diversos sistemas o IEEE estabeleceu o padrão POSIX threads (IEEE_1003.1c), ou Pthreads. Esse padrão define mais de 60 funções para criar e gerenciar threads. Tais funções são definidos na biblioteca pthreads.h. Além disso, a biblioteca define estruturas de dados e atributos para configurar os threads. De modo geral, esses atributos são passados como argumentos para os parâmetros das funções, por exemplo:

  • pthread_t: Handle para pthread, isto é, um valor que permite identificar o thread;
  • pthread_attr_t: Atributos para configuração de thread.

Esses recursos são utilizados nas principais funções para criação e gerenciamento de threads. Tais funções são apresentadas a seguir. Cabe ressaltar que as funções que retornam valor, tem como padrão o inteiro 0, indicando sucesso. Assim, qualquer inteiro diferente de zero é um código de erro.

Criando e destruindo threads

A função pthread_create é utilizada para inicializar um thread. Para isso, a função recebe como argumento o endereço de um dado pthread_t que será inicializado. Além disso, o endereço da função que será executada é informado no parâmetro start_routine. Tal função tem como retorno um valor void * e recebe apenas um argumento a partir do seu parâmetro void *. Esse argumento é passado para função start_routine especificando o último parâmetro da função pthread_create, denominado arg. Cabe ressaltar que essa função pode receber um atributo para configuração do thread. No entanto, esse argumento pode ser NULL, indicando que o thread será configurado conforme o padrão.

Um thread pode ser finalizado a partir da função pthread_exit. Essa função recebe como argumento um endereço que é utilizado para armazenar o valor de retorno do thread.

Gerenciando threads

Considerando um ambiente em que mais de um thread estão sendo executados, pode ser necessário, em algum momento, aguardar a finalização de um procedimento. Isso pode ser realizado com a função pthread_join. A função pthread_join recebe como argumentos a estrutura de controle do thread, do tipo pthread_t, que será finalizado e o endereço de um ponteiro (void **) para o valor de retorno do thread.

No contexto de execução do thread é possível obter o identificador do thread, denominado thread ID. Isso é realizado com a chamada pthread_self que tem como retorno um valor do tipo pthread_t.

Como dito anteriormente, um thread pode estar em diversos estados. Em sua execução o thread pode indicar para gerenciador que o mesmo pode ser bloqueado. Isso é realizado pela função sched_yield. Nesse caso, um outro thread entrará em execução.

Exemplos

Para exemplificar alguns conceitos apresentados e destacar a utilização da biblioteca pthreads, alguns programas em C foram desenvolvidos e compilados usando o GCC no ambiente Linux.

O primeiro exemplo demonstra a criação de threads e suas funções de gerenciamento, o objetivo é apenas mostrar a utilização das funções.

Para compilar esse arquivo, a seguinte linha de comando foi utilizada:

Já a execução do programa:

O resultado da execução do programa é mostrado abaixo. Como esperado, o thread é criado e a aplicação principal aguarda sua finalização.

Thread criado com sucesso!
Argumento: Minha primeira Thread!
Thread finalizado! Retorno = Minha primeira Thread!

No segundo exemplo, a mesma rotina é utilizada. No entanto, dois threads são criados.

O resultado da execução do programa é mostrado abaixo. Nesse programa, devido ao procedimento simples executado pela rotina, o thread foi finalizado antes que pudesse ser trocado por outro.

Thread A criado com sucesso!
Thread B criado com sucesso!
Thread B: 9
Thread B: 8
Thread B: 7
Thread B: 6
Thread B: 5
Thread B: 4
Thread B: 3
Thread B: 2
Thread B: 1
Thread B: 0
Thread A: 9
Thread A: 8
Thread A: 7
Thread A: 6
Thread A: 5
Thread A: 4
Thread A: 3
Thread A: 2
Thread A: 1
Thread A: 0
Thread A finalizado! Retorno = Thread A
Thread B finalizado! Retorno = Thread B

Isso pode ser modificado alterando a rotina de execução, adicionando a chamada da função sched_yield. Como dito anteriormente, essa função causa a interrupção do thread. 

O resultado da execução do programa é mostrado abaixo. Em comparação com o segundo caso, é possível observar que agora os threads tem sua execução alternada.

Thread A criado com sucesso!
Thread B criado com sucesso!
Thread B: 9
Thread A: 9
Thread B: 8
Thread A: 8
Thread B: 7
Thread A: 7
Thread B: 6
Thread A: 6
Thread B: 5
Thread A: 5
Thread B: 4
Thread A: 4
Thread B: 3
Thread A: 3
Thread B: 2
Thread A: 2
Thread B: 1
Thread A: 1
Thread B: 0
Thread A: 0
Thread A finalizado! Retorno = Thread A
Thread B finalizado! Retorno = Thread B

Conclusões parciais

Esse artigo teve como objeto fundamentar o conceito de threads e apresentar o padrão conhecido como POSIX Threads, também chamado de pthreads. No entanto, a aplicação desse recurso envolve outros aspectos que não foram abordados neste artigo. Pois, a partir do momento em que a aplicação é dividida em segmentos que são executados de forma alternada, e tais segmentos acessam a mesma informação, vem à tona questões voltadas ao compartilhamento de recursos e sincronização de operações. Essas características, se não forem tratadas corretamente, podem gerar uma grande confusão e, consequentemente, resultar em erro de execução de um programa.

Referências

Imagem destacada.

Veja + conteúdo

Fascinado por computação, especialmente na interface entre hardware e software, me engajei na área de sistemas embarcados. Atuo com desenvolvimento de sistemas embarcados e sou docente da Faculdade de Engenharia de Sorocaba.

Para mais informações: https://about.me/fdelunogarcia

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Comentários:
Notificações
Notificar
guest
1 Comentário
recentes
antigos mais votados
Inline Feedbacks
View all comments
zegotinha
zegotinha
24/10/2020 18:49

muito bom

Last edited 9 meses atrás by zegotinha
Talvez você goste:

Séries

Menu