C++ Threads com Time To Live (TTL)

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.

001_codigo_cpp

Inicialmente crie um projeto do DevCpp (http://sourceforge.net/projects/orwelldevcpp/) do tipo:
Console > C++

003_novoprojetocpp

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

Print de execução:
002_execucacao

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

Deixe um comentário