Threads em C++ no Linux
Uma thread é como uma linha de execução de um programa, que executa “paralelamente” com o restante do código do programa. A idéia é, a partir do ponto desejado, criar uma espécie de processo filho, que compartilhe os recursos com o programa pai. Porém, não é como a chamada “fork()”, que duplica o processo completo que está em execução. Em geral, o programa não executa “paralelamente”, tendo em vista que os computadores pessoais em geral, possuem apenas um processador. Mesmo assim, o escalonador de processos, dá a impressão de que os trechos de código estão realmente rodando em paralelo. No caso de computadores com mais de um processador, ou mais de um “core”, estes trechos de código podem realmente estar executando paralelamente.
O código que será mostrado aqui está longe de ser completo, mas vale para exemplificar o encapsulamento das chamadas básicas em uma classe.
Header
#ifndef THREAD_H #define THREAD_H #include <pthread.h> class Thread { public: Thread(); ~Thread(); void Start(); void Wait(); void Abort(); void Detach(); pthread_t GetId(); protected: static void *entryPoint(void *pthis); virtual void run() = 0; private: pthread_t m_threadId; }; #endif
Implementação
#include "thread.h" Thread::Thread() { } Thread::~Thread() { } /* static */ void *Thread::entryPoint(void *pthis) { Thread *ptr = static_cast<Thread *>(pthis); ptr->run(); } void Thread::Start() { int nRet; if (pthread_create(&m_threadId, NULL, entryPoint, this) != 0) { // throw an error } } void Thread::Wait() { if (pthread_join(m_threadId, NULL) != 0) { // throw an error } } void Thread::Abort() { if (pthread_cancel(m_threadId) != 0) { // throw an error } } void Thread::Detach() { if (pthread_detach(m_threadId) != 0) { // throw an error } } pthread_t Thread::GetId() { return m_threadId; }
Encapsulamento da classe
A classe Thread acima apresentada, implementa internamente as chamadas à biblioteca pthread, realizando todo o processo de criação e manipulação da thread transparentemente.
Note que o método “entryPoint” é estático, para que seja possível passá-lo como ponteiro de função para chamada “pthread_create”, e que ele recebe como parâmetro o argumento “this”, que representa a instância do objeto da thread.
A modelagem da classe segue a ideia da classe Thread da API da linguagem Java, ou seja, para a implementação de threads, deve-se escrever uma nova classe, que herde da classe Thread, e implemente o método “run()”, que será o ponto de entrada da nova thread
Exemplo:
#include <iostream> #include "thread.h" using namespace std; class SimpleThread: public Thread { private: void run() { cout << "Este codigo esta rodando "; cout << "em uma nova thread" << endl; sleep(5); cout << "Finalizando a execucao da thread" << endl; } };
Como pode ser visto acima, a classe SimpleThread implementa o método “run()”, que a partir de então roda em uma nova thread.
Funções utilizadas (pthread)
Para quem nunca programou threads em C, utilizei a biblioteca pthread, que fornece uma API para utilização de threads. Em seguida, vou explicar sucintamente a utilização de cada uma das chamadas utilizadas no código mostrado acima.
pthread_create:
Protótipo:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
Esta chamada, inicia uma nova thread, e invoca a chamada repassada em “start_routine”, passando “arg” como argumento.
Parâmetros:
thread:Endereço de memória para retornar o identificador da thread.
attr: Estrutura que contém atributos para a utilização na thread. Caso seja informado NULL, assume os atributos padrões.
start_routine:Ponteiro de função, a partir de onde será iniciada a nova thread.
arg: Parâmetro que será repassado para a função “start_routine”.
pthread_join:
Protótipo:
int pthread_join(pthread_t thread, void **retval); Esta chamada espera até que a thread especificada no primeiro argumento finalize.
Parâmetros:
thread: Identificador da thread.
retval: Se este parâmetro não for nulo, copia os dados retornados pela thread criadora.
pthread_cancel:
Protótipo:
int pthread_cancel(pthread_t thread);
Envia uma requisição de cancelamento para a thread. O atendimento ou não desta requisição, está atrelado aos atributos de inicialização da thread.
Parâmetros:
thread: Identificador da thread.
pthread_detach:
Protótipo:
int pthreada_detach(pthread_t thread);
Esta chamada libera os recursos associados à thread automaticamente assim que ela é finalizada.
Parâmetros:
thread: Identificador da thread.
Para mais detalhes, consulte as man pages da biblioteca pthread.
Como pode ser notado, o código aqui apresentado está incompleto, e não tem o intuito de ser uma referência única no uso de threads. Porém pode servir como uma idéia de design inicial para uma classe mais completa.
Abril 17, 2009 at 1:32 am
Nossa que legal.
a um tempo eu fiz uma classe assim la no trabalho.
Usei algumas coisas um pouco diferentes, mas de base é o mesmo.
Fiz um Stop, com o join, mas quando vai entrar no join precisa de uma verificação se a thread que esta dando o join não é ela mesma tentando dar join nela, que pelo menos la no trampo ela travou, e ficava esperando ela terminar.
E também fiz uns metodos do tipo waitTimedOut(int sec)
Se quiser eu posto aqui alguns metodos que eu fiz da classe e algumas coisas que eu criei. Precisei tambem de um evento que resolvi fazendo um semaforo compartilhado, ai a thread pode ser avisada por qualquer um.
Mas bem legal se eu tivesse visto esse post ano passado provavelmente eu tivesse pego o que vc fez.
Mas legal meus parabéns. Eu fiz uma classe Socket em C++.
Se tiver afim podemos juntar os códigos e criar um projetinho de classes a serem utilizadas para linux.
Eu to pensando em criar uma classe de pool de thread.