Threads em C/C++

Started by Dark_Side, 30 de November , 2006, 12:47:34 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Dark_Side

Hi,

Mais um pequeno artigo abordando a linguagem C ;)

A sintaxe da função SuspendThread é:

DWORD WINAPI SuspendThread(HANDLE thread_handle);
 
     thread_handle:
            handle da thread que se deseja pausar;

A função retorna o valor ZERO se a thread especificada for interrompida corretamente, ou retorna -1, se o processo falhar.

A função ResumeThread possui praticamente a mesma sintaxe:

DWORD WINAPI ResumeThread(HANDLE thread_handle);
 
     thread_handle:
            handle da thread que se deseja pausar;

A função também 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 função 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 função 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 têm 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 três elementos, fazemos a verificação das threads da seguinte maneira:

if(th[0] == 0 || th[1] == 0 || th[2] == 0) // Se alguma thread não tiver sido criada
 {
 MessageBox(0,"Ocorreu um erro ao criar as Threads","Erro",0x10); // Erro
 return 1; // Encerra
 }

Vamos dar atenção 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 estávamos manipulando apenas uma Thread, utilizamos a função WaitForSingleObject(), mas como estamos trabalhando com 3 ao invés de uma, utilizamos a função WaitForMultipleObjects() para aguardar o término da função de cada Thread.

Sintaxe:

QuoteBOOL WINAPI WaitForMultipleObjects(DWORD num_threads, HANDLE * thread_handle,BOOL esperar_todos, DWORD codigo_saida);


 num_threads:
       número de threads;

 thread_handle:
       array que contém 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 necessário até que a Thread seja finalizada, retornando um valor.


Nós declaramos uma variável auxiliar "i" e fazemos um laço 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
}

Não há segredo nas funções 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 própria.

É importante notar que, nem sempre, as mensagens irão aparecer na ordem:

QuoteMensagem 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 são executadas.

Geralmente, uma thread é criada com prioridade normal.
Utilizando a função SetThreadPriority() podemos alterar a prioridade de execução 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 função SetThreadPriority():

SetThreadPriority(th,THREAD_PRIORITY_LOWEST);
Muda a prioridade da Thread para BAIXA. No conjunto de prioridades, temos:

QuoteTHREAD_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 também ao manipulá-las; como por exemplo evitar que duas ou mais Threads acessem uma mesma região da memória, "disputem" tarefas entre si, dentre outros.

Os exemplos apresentados ilustram o funcionamento de threads locais, isto é, criadas e utilizadas dentro do próprio aplicativo. Muitas técnicas como a DLL Injection - que consiste em injetar uma DLL determinada em um processo específico - criam Threads Remotas, isto é, threads que são criadas e executadas em um outro processo. Geralmente, utilize-se a função VirtualAllocEx() para alocar um espaço virtual neste processo e escrever dados no endereço retornado, sucedida da função CreateRemoteThread() - a responsável por criar Threads Remotas.
No entanto, não abordarei a função neste artigo =)

Bem, é isso...
Bye.

insanity

he he, parabens denovo, ando sempre lendo seus topicos ;)