Este tutorial tem por objetivo demonstrar o controle de threads em C++ com o uso de uma thread monitora (pthread_t _killthis) em conjunto com o conceito de ‘TTL’ ou Time to Live – tempo de vida da thread. A ideia central é: Execute o meu método (ponteiro de função) em uma thread até que ele termine, ou que, o tempo definido (em segundos) se esgote.
Este artigo envolve conceitos de threads (pthread_t), ponteiros (*), ponteiro de função (void (*pfuntoexecute)(void*)), classes, sleep, parameters do tipo void* (reinterpret_cast…), join de threads (pthread_join), etc, que não serão explicados neste tutorial.
Inicialmente crie um projeto do DevCpp (http://sourceforge.net/projects/orwelldevcpp/) do tipo:
Console > C++
Código da classe main.cpp:
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <sys/ioctl.h> #include <sys/fcntl.h> //#include <signal.h> // sigaction(), sigsuspend(), sig*() #include <unistd.h> // alarm(), sleep /* Emerson Shigueo Sugimoto - eme.vbnet[at]gmail.com 08/08/2014 21:12 */ class ThreadTTL { public: ThreadTTL(){ SetTTL(2); } ThreadTTL(unsigned int ttl = 2){ SetTTL(ttl <= 0 ? 2 : ttl); } ThreadTTL(unsigned int ttl, void (*pfuntoexecute)(void*), void* argumentos){ SetTTL(ttl <= 0 ? 2 : ttl); SetFunctionToExecute(pfuntoexecute, argumentos); } pthread_t InitThread(void){ if (_pfuntoexecute == NULL){return 0;} initthreadttl(); //TTL pthread_create(&_mythread, NULL, callexecutethreadmethod, reinterpret_cast<void*>(this)); return _mythread; }; void StopThread(void){ if (_mythread != NULL) { pthread_cancel(_mythread); pthread_kill(_mythread, 0); } if (_killthis != NULL) { pthread_cancel(_killthis); pthread_kill(_killthis, 0); } }; void SetTTL(unsigned int ttl){ _ttl = ttl; }; unsigned int GetTTL(void) { return _ttl; }; void SetFunctionToExecute(void (*pfuntoexecute)(void*), void* argumentos){ _pfuntoexecute = pfuntoexecute; _argumentos = argumentos; }; private: unsigned int _ttl; pthread_t _mythread, _killthis; void (*_pfuntoexecute)(void*); void* _argumentos; void executethreadmethod(void){ if (_pfuntoexecute != NULL){ _pfuntoexecute(_argumentos); } StopThread(); }; static void* callexecutethreadmethod(void* args){ ThreadTTL* tth = reinterpret_cast<ThreadTTL*>(args); tth->executethreadmethod(); return 0; }; //-- thread kill this void initthreadttl(void){ pthread_create(&_killthis, NULL, executethreadttl, reinterpret_cast<void*>(this)); }; static void* executethreadttl(void* args){ ThreadTTL* tth = reinterpret_cast<ThreadTTL*>(args); sleep(tth->GetTTL()); //TTL tth->StopThread(); return 0; }; }; //---------------- // métodos //---------------- void executesomething(void* args){ //should run for 50s... for(int i = 0; i < 50; i++) { printf("[th] handling something...%d\n", i); sleep(1); } } void executesomething2(void* args){ //should run for 50s... int* valor = reinterpret_cast<int*>(args); for(int i = 0; i < 50; i++) { printf("[th2 - %d] valor: %d\n", i, *valor); sleep(1); } } //uma struct de parameter struct ParameterMoreComplex{ int valor1; std::string str1; bool t; float f; }; void executesomething3(void* args){ //should run for 50s... struct ParameterMoreComplex* pmc = reinterpret_cast<struct ParameterMoreComplex*>(args); for(int i = 0; i < 50; i++) { printf("[th3 - %d] ParameterMoreComplex: %d, %s, %s, %f\n", i, pmc->valor1, pmc->str1.c_str(), pmc->t ? "T" : "F", pmc->f); sleep(1); } } int main(int argc, char *argv[]) { ThreadTTL* tth = new ThreadTTL(15, executesomething, NULL); tth->InitThread(); int valor = 154; ThreadTTL* tth2 = new ThreadTTL(7, executesomething2, reinterpret_cast<void*>(&valor)); tth2->InitThread(); struct ParameterMoreComplex pmc; pmc.valor1 = 140; pmc.str1 = "methodo3"; pmc.t = true; pmc.f = 10.23; ThreadTTL* tth3 = new ThreadTTL(8, executesomething3, reinterpret_cast<void*>(&pmc)); tth3->InitThread(); printf("main sleep(50)\n"); sleep(50); //-- kill if you like with ctrl+c //pthread_join(thread1, NULL); return 0; }
A classe principal que controla o TTL das threads é: ThreadTTL.
A thread pthread_t _mythread é responsável por executar o método definido no ponteiro de função: void (*pfuntoexecute)(void*).
Caso o método do ponteiro de função demore mais do que o TTL definido (em segundos), a thread pthread_t _killthis é responsável por ‘matar’ o processo.
O método void StopThread(void) é responsável por matar as threads _mythread e _killthis.
Exemplo de função a ser passada para o ponteiro de função:
//uma struct de parameter struct ParameterMoreComplex{ int valor1; std::string str1; bool t; float f; }; void executesomething3(void* args){ //should run for 50s... struct ParameterMoreComplex* pmc = reinterpret_cast<struct ParameterMoreComplex*>(args); for(int i = 0; i < 50; i++) { printf("[th3 - %d] ParameterMoreComplex: %d, %s, %s, %f\n", i, pmc->valor1, pmc->str1.c_str(), pmc->t ? "T" : "F", pmc->f); sleep(1); } }
Exemplo de chamada na main:
struct ParameterMoreComplex pmc; pmc.valor1 = 140; pmc.str1 = "methodo3"; pmc.t = true; pmc.f = 10.23; ThreadTTL* tth3 = new ThreadTTL(8, executesomething3, reinterpret_cast<void*>(&pmc)); tth3->InitThread();
Como não existe nenhum join das threads (pthread_join), adicione um sleep na sua main, apenas para não matar o processo:
printf("main sleep(50)\n"); sleep(50); //-- kill if you like with ctrl+c
Sugestões e melhorias são bem aceitas. ps: Tentei simular o mesmo efeito com o uso de sigaction + alarm (SO linux), porém devido a assinatura do handler (void(int)) não consegui controlar a área de memória dentro da classe, para matar a thread específica.
Att,
Emerson