You are on page 1of 13

Hi, Mais um pequeno artigo abordando a linguagem C ;). Hoje, veremos como utilizar Threads em C/C++.

Basicamente, uma Thread pode ser considerada um subprocesso dentro de um aplicativo. Pode ser considerada como uma funo, porm, de certa forma, independente. O uso de Threads pode proporcionar uma certa otimizao em um programa, uma vez em que duas ou mais tarefas podem ser executadas ao mesmo tempo. Todo programa, possui uma Thread principal, destinada a uma funo tambm principal, geralmente a main(). Vamos tomar o seguinte exemplo inicial:

#include <windows.h> void msg() { MessageBox(0,"MENSAGEM DA FUNO","lol",0); } int main() { MessageBox(0,"MENSAGEM PRINCIPAL","lol",0); msg(); return 0; } Em situao normal, o program acima mostraria, invariavelmente, as mensagens : "MENSAGEM PRINCIPAL" e "MENSAGEM DA FUNO", respectivamente. Isso ocorre pois, primeiramente, a instruo MessageBox() da funo main() executada, e logo aps, a funo "msg()" chamada. Utilizando Threads, podemos fazer com que as duas mensagens sejam mostradas praticamente ao mesmo tempo =). Veremos isso mais adiante. Em ambiente Windows, temos, basicamente, 4 funes para trabalhar com Threads todas definidas em "WINDOWS.H", so elas:

CreateThread - utilizada para criar uma thread. SuspendThead - pausa a execuo de uma thread. ResumeThread - reinia a exeuo de uma thread previamente pausada. TerminateThread - termina uma thread. Vamos ver como criar uma Thread:

#include <windows.h> DWORD WINAPI Thread(LPVOID); int main() { DWORD id; HANDLE th = CreateThread(0,0,Thread,0,0,&id);

if(th == 0) { MessageBox(0,"Ocorreu um erro ao criar a Thread","Erro",0x10); return 1; } MessageBox(0,"Mensagem da funo MAIN","LOL",0x40); WaitForSingleObject(th,INFINITE); TerminateThread(th,0); CloseHandle(th); return 0; } DWORD WINAPI Thread(LPVOID p) { MessageBox(0,"Mensagem da Thread","LOL",0x40); return 0; } O programa acima ilustra a situao inicial colocada no incio artigo. Se voc execut-lo, ver que a mensagem da funo main e a mensagem da thread criada so mostradas praticamente ao mesmo tempo. Vamos analisar trecho por trecho:

#include <windows.h> Como j havia dito, as funes que iremos utilizar para manipular threads esto definidas em "windows.h". DWORD WINAPI Thread(LPVOID); Temos acima a definio da funo referente Thread. A API do Windows utiliza ponteiros para controlar uma Thread. um ponteiro para funo como o abaixo: DWORD (WINAPI *LPTHREAD_START_ROUTINE)(LPVOID) Esse ponteiro requer uma funo do tipo DWORD, definida por WINAPI e que contenha um parmetro - ponteiro para um argumento do tipo VOID. Note que "LPVOID" equivale a "void *". DWORD id; A varivel "id" ir armazenar o nmero ID da Thread que ser criada. HANDLE th = CreateThread(0,0,Thread,0,0,&id); Esta linha responsvel por criar nossa thread. A sua sintaxe : HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES ThreadAtt, SIZE_T StackSize, LPTHREAD_START_ROUTINE Addr_Funcao, LPVOID argumento,

DWORD flags, LPDWORD ThreadId ); ThreadAtt: ponteiro para uma estrutura do tipo LPSECURITY_ATTRIBUTES que contm atributos de segurana da Thread. Se passado como ZERO, a Thread criada com atributos padres. StackSize: tamanho inicial da stack em bytes. Se ZERO, o sistema utiliza um tamanho padro. Argumento: um argumento opcional para a funo. Flags: define o comportamento da Thread logo aps ser criada. Se a flag "CREATE_SUSPENDED" for definida, a thread pausada assim que criada, permanecendo neste estado at que a funo ResumeThread() seja chamada. Se for ZERO, a thread executada logo aps ser criada. ThreadId: ponteiro para uma varivel do tipo DWORD que ir armazenar a identificao da Thread. Se ZERO, o argumento ignorado. A funo CreateThread() retorna um Handle para a thread criada, do contrrio, retornado um Handle Nulo, ou seja, ZERO. Cientes dos valores de retorno da funo, podemos saber se a Thread foi criada com sucesso ou no:

if(th == 0) // Se o valor retornado for ZERO { MessageBox(0,"Ocorreu um erro ao criar a Thread","Erro",0x10); // Mostra erro return 1; // Encerra programa } Aps criamos nossa Thread (que executada imediatamente aps ser criada, devido ao valor ZERO passado ao argumento Flags), chamamos a funo MessageBox() dentro da funo main(): MessageBox(0,"Mensagem da funo MAIN","LOL",0x40); O trecho abaixo responsvel por aguardar o trmino da Thread e termin-la:

WaitForSingleObject(th,INFINITE); TerminateThread(th,0); CloseHandle(th); A funo WaitForSingleObject() faz com que o programa aguarde um determinado ou um inderterminado tempo, a execuo da Thread. Sintaxe: DWORD WINAPI WaitForSingleObject(HANDLE thread_handle,DWORD tempo); thread_handle: handle da Thread; tempo:

tempo em milisegundos que o programa deve aguardar. Caso a constante "INFINITE" seja especificada, o programa aguarda o tempo necessrio at que a Thread seja finalizada, retornando um valor.

Para terminarmos uma Thread, utilizamos a funo TerminateThread() cuja sintaxe :

BOOL WINAPI TerminateThread(HANDLE thread_handle,DWORD codigo_saida); thread_handle: handle da Thread; codigo_saida: cdigo de retorno. 0 = sucesso; 1 = erro. Se a funo tiver xito um valor diferente de ZERO retornado; do contrrio ZERO retornado. Finalmente, fechamos o Handle da Thread: CloseHandle(th); A sintaxe : CloseHandle(thread_handle); Onde "thread_handle" a varivel que contm o handle da Thread. A funo da thread bem simples:

DWORD WINAPI Thread(LPVOID p) { MessageBox(0,"Mensagem da Thread","LOL",0x40); return 0; } Apenas mostra uma mensagem e encerra. Note que quando a funo chega instruo "return 0", implicitamente, estamos finalizando a Thread. Voc deve estar se perguntando: Mas se quando a thread retorna um valor, quer dizer que ela foi encerrada. Ento qual a razo para utilizar TerminateThread() e CloseHandle()? A thread j no foi finalizada? A resposta para as questes simples: Quando uma thread finalizada (retornando um valor na funo), o objeto da thread continua associado ao processo. Para que realmente sua referncia seja removida, devemos utilizar a funo TerminateThead() sucedida da funo CloseHandle() para fechar seu handle. Observe ainda que a funo possui um argumento: "LPVOID p". O que veremos a seguir como podemos fazer uso deste. O tipo de dado VOID considerado um tipo vazio. O valor de uma varivel do tipo VOID pode ser transformado em qualquer outro tipo de dado. Exemplo: void mostra_valor(LPVOID valor) { printf("%d",(int)valor); } Neste caso, se passarmos para a funo o valor 10 por exemplo, este seria convertido e mostrado

como inteiro: (int)valor - typecast. Com Threads, podemos fazer o mesmo, veja:

#include <windows.h> #include <stdio.h> // para usar printf() =) DWORD WINAPI Thread(LPVOID); int main() { DWORD id; int valor = 10; HANDLE th = CreateThread(0,0,Thread,(LPVOID)valor,0,&id); if(th == 0) { MessageBox(0,"Ocorreu um erro ao criar a Thread","Erro",0x10); return 1; } WaitForSingleObject(th,INFINITE); TerminateThread(th,0); CloseHandle(th); return 0; } DWORD WINAPI Thread(LPVOID p) { printf("O valor passado e': %d",(int)p); return 0; } Rode o programa e veja o resultado. Vamos ver os pontos importantes: int valor = 10; Nos defnimos a varivel "valor" que ser passada como argumento. HANDLE th = CreateThread(0,0,Thread,(LPVOID)valor,0,&id); Note na funo acima a varivel "valor" sendo passada como argumento. Repare ainda o TYPECAST: (LPVOID)valor Estamos convertendo o valor para LPVOID ou VOID * - tipo requerido pela Thread. Na funo da Thread temos:

DWORD WINAPI Thread(LPVOID p) { printf("O valor passado e': %d",(int)p); return 0; }

A varivel "p" contm o valor passado funo: 10. Este mostrado pela linha: printf("O valor passado e': %d",(int)p); Note a converso de tipo: (int)p - typecast de void para inteiro. Como havia dito no incio do artigo, temos duas funes utilizadas para pausar e resumir a execuo de uma Thread: SuspendThread() e ResumeThread(), respectivamente. Vamos ver um exemplo de uso das funes:

#include <windows.h> #include <stdio.h> // printf() #include <stdlib.h> // system() DWORD WINAPI Thread(LPVOID); int main() { DWORD id; HANDLE th = CreateThread(0,0,Thread,0,0,&id); if(th == 0) { MessageBox(0,"Ocorreu um erro ao criar a Thread","Erro",0x10); return 1; } while(1) { printf("Pressione qualquer tecla para pausar a thread...\n"); system("pause > nul"); SuspendThread(th); printf("Pressione qualquer tecla para resumir a thread...\n"); system("pause > nul"); ResumeThread(th); } WaitForSingleObject(th,INFINITE); TerminateThread(th,0); CloseHandle(th); return 0; } DWORD WINAPI Thread(LPVOID p) { while(1) { Sleep(1000); MessageBox(0,"Thread em execuo!","LOL",0x40); } return 0; } O programa cria uma thread que, dentro de um intervalo de 1 segundo (1000 milisegundos = 1 sec), mostra uma mensagem. Vamos destacar o seguinte trecho:

while(1)

{ printf("Pressione qualquer tecla para pausar a thread...\n"); system("pause > nul"); SuspendThread(th); // Pausa a Thread printf("Pressione qualquer tecla para resumir a thread...\n"); system("pause > nul"); ResumeThread(th); // Reinicia a Thread } Acima, h um loop no qual temos a opo de pausar ou resumir a thread. Primeiramente o programa pede o pressionamento de uma tecla para que a Thread seja interrompida e, em seguida, novamente o programa pede o pressionamento de uma outra tecla pra resumir a execuo da Thread. Observe a linha: system("pause > nul"); Esta chama pelo comando "pause"; o parmetro " > nul" utilizado para ocultar a mensagem "Pressione qualquer tecla para continuar..." - uma vez em que j mostramos uma mensagem semelhante anteriormente com printf() ;) A sintaxe da funo SuspendThread :

DWORD WINAPI SuspendThread(HANDLE thread_handle); thread_handle: handle da thread que se deseja pausar; A funo retorna o valor ZERO se a thread especificada for interrompida corretamente, ou retorna -1, se o processo falhar. A funo ResumeThread possui praticamente a mesma sintaxe:

DWORD WINAPI ResumeThread(HANDLE thread_handle); thread_handle: handle da thread que se deseja pausar; A funo tambm retorna o valor ZERO se a thread for resumida corretamente, ou retorna -1, se falhar.

At agora, os exemplos mostraram como utilizar uma Thread. Veremos como proceder com duas ou mais Threads. Exemplo:

#include <windows.h> DWORD WINAPI Thread1(LPVOID); DWORD WINAPI Thread2(LPVOID); DWORD WINAPI Thread3(LPVOID); int main() { DWORD id[3]; HANDLE th[3];

th[0] = CreateThread(0,0,Thread1,0,0,&id[0]); th[1] = CreateThread(0,0,Thread2,0,0,&id[1]); th[2] = CreateThread(0,0,Thread3,0,0,&id[2]); if(th[0] == 0 || th[1] == 0 || th[2] == 0) { MessageBox(0,"Ocorreu um erro ao criar as Threads","Erro",0x10); return 1; } MessageBox(0,"Mensagem da funo MAIN","LOL",0x40); WaitForMultipleObjects(3,th,1,INFINITE); int i; for(i = 0;i<3;i++) { TerminateThread(th[i],0); CloseHandle(th[i]); } return 0; } DWORD WINAPI Thread1(LPVOID p) { MessageBox(0,"Mensagem da Thread 1","LOL",0x40); return 0; } DWORD WINAPI Thread2(LPVOID p) { MessageBox(0,"Mensagem da Thread 2","LOL",0x40); return 0; } DWORD WINAPI Thread3(LPVOID p) { MessageBox(0,"Mensagem da Thread 3","LOL",0x40); return 0; }

Temos um exemplo de um programa que cria 3 threads =). Vamos analisar:

DWORD WINAPI Thread1(LPVOID); DWORD WINAPI Thread2(LPVOID); DWORD WINAPI Thread3(LPVOID); Definimos cada funo de cada Thread: Thread1,Thread2,Thread3.

DWORD id[3]; HANDLE th[3]; Como vamos criar 3 threads utilizaremos arrays para armazenar, os handlers e ID's de cada thread. Note que cada array possui 3 elementos e que tm os ndices: 0, 1 e 2.

th[0] = CreateThread(0,0,Thread1,0,0,&id[0]); // Cria a primeira thread th[1] = CreateThread(0,0,Thread2,0,0,&id[1]); // Cria a segunda thread th[2] = CreateThread(0,0,Thread3,0,0,&id[2]); // Cria a teceira thread Portanto: th[0] = handle da primeira thread; id[0] = ID da primeira thread; th[1] = handle da segunda thread; id[1] = ID da segunda thread; th[2] = handle da terceira thread; id[2] = ID da terceira thread; Note que como temos um array contendo trs elementos, fazemos a verificao das threads da seguinte maneira:

if(th[0] == 0 || th[1] == 0 || th[2] == 0) // Se alguma thread no tiver sido criada { MessageBox(0,"Ocorreu um erro ao criar as Threads","Erro",0x10); // Erro return 1; // Encerra } Vamos dar ateno ao seguinte trecho que termina as threads e fecha seus handlers: WaitForMultipleObjects(3,th,INFINITE); int i; for(i = 0;i<3;i++) { TerminateThread(th[i],0); CloseHandle(th[i]); } Quando estvamos manipulando apenas uma Thread, utilizamos a funo WaitForSingleObject(), mas como estamos trabalhando com 3 ao invs de uma, utilizamos a funo WaitForMultipleObjects() para aguardar o trmino da funo de cada Thread. Sintaxe:

BOOL WINAPI WaitForMultipleObjects(DWORD num_threads, HANDLE * thread_handle,BOOL esperar_todos, DWORD codigo_saida); num_threads: nmero de threads; thread_handle: array que contm os handlers das threads; esperar_todos: Pode ser TRUE (1) ou FALSE (0). Se 1, o programa aguarda at todos os objetos, no caso as Threads sejam finalizadas; Se 0, o programa aguarda at que alguma das threads finalize. tempo:

tempo em milisegundos que o programa deve aguardar. Caso a constante "INFINITE" seja especificada, o programa aguarda o tempo necessrio at que a Thread seja finalizada, retornando um valor.

Ns declaramos uma varivel auxiliar "i" e fazemos um lao for que vai de 0 a 2 - ndices do array "th":

int i; for(i = 0;i<3;i++) { TerminateThread(th[i],0); // Termina a thread identificado por th[i] CloseHandle(th[i]); // Fecha o handle desta mesma thread } No h segredo nas funes de cada thread, veja:

DWORD WINAPI Thread1(LPVOID p) { MessageBox(0,"Mensagem da Thread 1","LOL",0x40); return 0; } DWORD WINAPI Thread2(LPVOID p) { MessageBox(0,"Mensagem da Thread 2","LOL",0x40); return 0; } DWORD WINAPI Thread3(LPVOID p) { MessageBox(0,"Mensagem da Thread 3","LOL",0x40); return 0; }

Cada Thread mostra uma mensagem relativa a ela prpria. importante notar que, nem sempre, as mensagens iro aparecer na ordem:

Mensagem da Thread 1 Mensagem da Thread 2 Mensagem da Thread 3 Isso vai depender do escalonamento de tempo do processador, que seria uma escala na qual os processos so executadas. Geralmente, uma thread criada com prioridade normal. Utilizando a funo SetThreadPriority() podemos alterar a prioridade de execuo de uma Thread. Um exemplo: #include <windows.h> #include <stdio.h> // printf() #include <stdlib.h> // system()

DWORD WINAPI Thread1(LPVOID); int main() { DWORD id; HANDLE th; th = CreateThread(0,0,Thread1,0,0,&id); if(th == 0) { MessageBox(0,"Ocorreu um erro ao criar as Threads","Erro",0x10); return 1; } SetThreadPriority(th,THREAD_PRIORITY_LOWEST); printf("Pressione qualquer tecla para sair..."); system("pause > nul"); TerminateThread(th,0); CloseHandle(th); return 0; } DWORD WINAPI Thread1(LPVOID p){ while(1) { Sleep(1000); MessageBox(0,"Mensagem da Thread 1","LOL",0x40); } return 0;} Um exemplo j visto anteriormente adaptado. O programa cria uma thread que mostra uma mensagem a cada 1 segundo. Ao pressionar uma tecla, a thread finalizada e o programa encerrado. O nico detalhe a funo SetThreadPriority(): SetThreadPriority(th,THREAD_PRIORITY_LOWEST); Muda a prioridade da Thread para BAIXA. No conjunto de prioridades, temos:

THREAD_PRIORITY_LOWEST -> baixa THREAD_PRIORITY_BELOW_NORMAL -> abaixo do normal THREAD_PRIORITY_NORMAL -> normal THREAD_PRIORITY_ABOVE_NORMAL -> acima do normal THREAD_PRIORITY_HIGHEST -> alta THREAD_PRIORITY_TIME_CRITICAL -> tempo real Deve-se tomar muito cuidado ao alterar a prioridade de threads, principalmente se esta for alta. Pode ocorrer, por exemplo, ao cosumo de todos os recursos de processamento do computador, tornando-o mais lento ou at mesmo levar ao seu travamento. Quanto s Threads, devemos tomar muito cuidado tambm ao manipul-las; como por exemplo evitar que duas ou mais Threads acessem uma mesma regio da memria, "disputem" tarefas entre si, dentre outros. Os exemplos apresentados ilustram o funcionamento de threads locais, isto , criadas e utilizadas dentro do prprio aplicativo. Muitas tcnicas como a DLL Injection - que consiste em injetar uma DLL determinada em um processo especfico - criam Threads Remotas, isto , threads que so criadas e executadas em um outro processo. Geralmente, utilize-se a funo VirtualAllocEx() para alocar um espao virtual neste processo e escrever dados no endereo retornado, sucedida da

funo CreateRemoteThread() - a responsvel por criar Threads Remotas. No entanto, no abordarei a funo neste artigo =) Bem, isso... Bye. Djack 30 Nov 2006, 10:11 Vlw dark side... era o que eu estava procurando, o de delphi eu ja sabia mas em c pra mim e novo... VLW. Sempre postando coisas novas. PerNa Mais um pra coletanea... DarthBalls 12 Dec 2006, 10:45 Aew... Tava vendo esse topic e comecei a pensa... Tpw, tem uns programas q vc usa WinMAIN, q sao os que usam a API do windows neh?? esses ae usam o main mesmo e usam funoes do windows Qnd q eu devo usa main e WinMain?? posso usa func do win com main normal?? dark_side 12 Dec 2006, 22:28 Aew... Tava vendo esse topic e comecei a pensa... Tpw, tem uns programas q vc usa WinMAIN, q sao os que usam a API do windows neh?? esses ae usam o main mesmo e usam funoes do windows Qnd q eu devo usa main e WinMain?? posso usa func do win com main normal?? Hi, Programas que iniciam pela funo WinMain(), geralmente, so programas escritos como Windows Application, ist , um programa prprio para Windows, e no um console. No entanto, no necessariamente, somente um programa onde consta funo WinMain() pode utilizar a API do Windows. Um exemplo: #include <windows.h> int main() { MessageBox(0,'hi...','lol',64); return 0; } O programa acima poderia ser compilado normalmente no modo console e mesmo assim utilizaria a API do Windows - no caso a funo MessageBox que responsvel por mostrar uma mensagem na tela. O uso da API do windows pode ser feito mediante a declarao do header "windows.h", ou de headers adicionais, dependendo da funo. Pode-se dizer que a funo WinMain() a equivalente da funo main() para aplicativos prprios para o Windows. Dependendo do compilador, pode-se at utilizar qualquer uma das duas para qualquer tipo de aplicao. Mas no geral, utiliza-se a funo main() quando estamos escrevendo um programa para console e WinMain() quando se trata de um programa Windows, principalmente porque esta ltima funo precisa ser declarada com determinados parmetros que geralmente so usados em apenas aplicativos Windows. 30 Nov 2006, 15:25

You might also like