sessão 5 – Gestão básica de threads em Win32
Bibliografia de apoio:
Capítulos 6 e 8 do Livro Windows System Programming (da bibliografia)
MSDN:
Synchronization Objects https://docs.microsoft.com/pt-pt/windows/win32/sync/synchronization-objects
Wait Functions https://msdn.microsoft.com/en-us/library/windows/desktop/ms687069(v=vs.85).aspx
Time Functions https://docs.microsoft.com/en-us/windows/win32/sysinfo/time-functions
Criar e sincronizar threads:
modelo de exclusão mutua ( com mutexes, um semáforo simplificado )
existe uma zona de memória que é partilhada entre threads mas que deve ser usada apenas quando está livre e trancada quando está ocupada (secção critica)
e serve para o mesmo processo ou entre processos
uma thread suspensa podem voltar com ResumeThread( HANDLE )
#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#define TAM 200
#define MAX_THREADS 2
BOOL sairThread = FALSE;
typedef struct {
DWORD total_soma;
int lim_inf, lim_sup;
} dados_thread;
DWORD WINAPI SomaPares(LPVOID lpParametro)
{
dados_thread* dados = (dados_thread*) lpParametro;
_tprintf(TEXT("Sou a thread %d somar pares de %d a %d\n"), GetCurrentThreadId(), dados->lim_inf, dados->lim_sup);
for (int i = dados->lim_inf; i <= dados->lim_sup; i++)
{
if (i % 2 == 0){
dados->total_soma += i;
}
if (i % 200 == 0){
Sleep(1000);
}
}
return 0;
}
int _tmain(int argc, TCHAR* argv[]) {
HANDLE hThreadArray[MAX_THREADS]; //handle threads
dados_thread dado[MAX_THREADS];
DWORD tid[MAX_THREADS], resultado;
#ifdef UNICODE
_setmode(_fileno(stdin), _O_WTEXT);
_setmode(_fileno(stdout), _O_WTEXT);
_setmode(_fileno(stderr), _O_WTEXT);
#endif
//pedir aqui ao utilizador os intervalos
_tprintf(TEXT("qual o valor MIN do 1º intervalo\n"));
wscanf_s(TEXT("%d"), &dado[0].lim_inf);
_tprintf(TEXT("qual o valor MAX do 1º intervalo\n"));
wscanf_s(TEXT("%d"), &dado[0].lim_sup);
_tprintf(TEXT("qual o valor MIN do 2º intervalo\n"));
wscanf_s(TEXT("%d"), &dado[1].lim_inf);
_tprintf(TEXT("qual o valor MAX do 2º intervalo\n"));
wscanf_s(TEXT("%d"), &dado[1].lim_sup);
dado[0].total_soma = dado[1].total_soma = 0;
hThreadArray[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SomaPares, &dado[0], 0, &(tid[0]));
hThreadArray[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SomaPares, &dado[1], 0, &(tid[1]));
if (hThreadArray[0] == NULL || hThreadArray[1] == NULL)
{
_tprintf(TEXT("nada de threads"));
return -1;
}
//esperar pela primeira para terminar
resultado = WaitForMultipleObjects(2, hThreadArray, FALSE, INFINITE);
if(resultado == WAIT_OBJECT_0)
{
_tprintf(TEXT("resultado da thread[%d]: %d\n"), tid[0], dado[0].total_soma);
_tprintf(TEXT("resultado da thread[%d]: %d\n"), tid[1], dado[1].total_soma);
}
CloseHandle(hThreadArray[0]);
CloseHandle(hThreadArray[1]);
return 0;
}
como medir o tempo de execução de um programa no windows?
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main(void)
{
LARGE_INTEGER frequencia, inicio, fim;
double intervalo;
QueryPerformanceFrequency(&frequencia); // obter ticks por segundo
QueryPerformanceCounter(&inicio); //obter ticks iniciais
//executar o codigo
QueryPerformanceCounter(&fim); // obter ticks finais
intervalo = (double)(fim.QuadPart - inicio.QuadPart) / frequencia.QuadPart; //obter tempo em segundos
printf("O codigo demorou %.3f segundos a executar.\n", intervalo);
return 0;
}
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#define MAX_THREADS 20
typedef struct {
unsigned int limiteBaixo;
unsigned int limiteAlto;
unsigned int* contadorPartilhado;
} TDados;
DWORD WINAPI ContaMultiplosTres(LPVOID lmParam)
{
unsigned int i;
TDados* data = (TDados*)lmParam;
for (i = data->limiteBaixo; i <= data->limiteAlto; i++)
{
if (i % 3 == 0)
{
(*(data->contadorPartilhado))++;
}
}
return 0;
}
int _tmain(int argc, TCHAR* argv[])
{
HANDLE hThreads[MAX_THREADS];
TDados tdados[MAX_THREADS];
unsigned int numeroThreads, i, limSuperior = 400000000, contador = 0;
LARGE_INTEGER tickSegundo, inicio, fim;
double duracao;
#ifdef UNICODE
_setmode(_fileno(stdin), _O_WTEXT);
_setmode(_fileno(stdout), _O_WTEXT);
_setmode(_fileno(stderr), _O_WTEXT);
#endif
if (!QueryPerformanceCounter(&tickSegundo))
{
_tprintf(TEXT("Erro: obter ticks por segundo.\n"));
}
_tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS);
_tscanf_s(TEXT("%u"), &numeroThreads);
//lançar as threads (suspensas)
for (i = 0; i < numeroThreads; i++)
{
tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i;
tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1);
tdados[i].contadorPartilhado = &contador;
_tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto);
hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL);
}
//começar o cronometro, ativar as threadas
QueryPerformanceCounter(&inicio);
for (i = 0; i < numeroThreads; i++) //dividir os intervalos
{
ResumeThread(hThreads[i]); //ativar as threads
}
WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem
//esperar que as threads terminem, ler o cronometro
QueryPerformanceCounter(&fim);
duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart;
_tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao);
for (i = 0; i < numeroThreads; i++)
{
CloseHandle(hThreads[i]);
}
//existe um problema de sincronização
//falta sincronização das threads
//o numero ideal de threads é igual ao numero de cores do processador
//4 cores, 4 threads
//4 cores, 5 thread, fica mais lento...
//o escalonamento de processos..
return 0;
}
//usar mutexes para garantir acesso em exlucsão mutua ao contador global
//createMutexA, waitForSingleObject, ReleaseMutex, CloseHandle
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#define MAX_THREADS 20
typedef struct {
unsigned int limiteBaixo;
unsigned int limiteAlto;
unsigned int* contadorPartilhado;
//receber o mutex
HANDLE mutexPartilhado;
} TDados;
DWORD WINAPI ContaMultiplosTres(LPVOID lmParam)
{
unsigned int i;
TDados* data = (TDados*)lmParam;
for (i = data->limiteBaixo; i <= data->limiteAlto; i++)
{
if (i % 3 == 0)
{
//esperar no mutex, incrementar, libertar
WaitForSingleObject(data->mutexPartilhado, INFINITE);
(*(data->contadorPartilhado))++;
ReleaseMutex(data->mutexPartilhado);
}
}
return 0;
}
int _tmain(int argc, TCHAR* argv[])
{
HANDLE hThreads[MAX_THREADS];
TDados tdados[MAX_THREADS];
unsigned int numeroThreads, i, limSuperior = 4000000, contador = 0;
LARGE_INTEGER tickSegundo, inicio, fim;
double duracao;
//o mutex
HANDLE mutex;
#ifdef UNICODE
_setmode(_fileno(stdin), _O_WTEXT);
_setmode(_fileno(stdout), _O_WTEXT);
_setmode(_fileno(stderr), _O_WTEXT);
#endif
if (!QueryPerformanceCounter(&tickSegundo))
{
_tprintf(TEXT("Erro: obter ticks por segundo.\n"));
}
_tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS);
_tscanf_s(TEXT("%u"), &numeroThreads);
//criar o mutex
mutex = CreateMutex(NULL, FALSE, NULL);
//lançar as threads (suspensas)
for (i = 0; i < numeroThreads; i++)
{
tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i;
tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1);
tdados[i].contadorPartilhado = &contador;
//passar o mutex
tdados[i].mutexPartilhado = mutex;
_tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto);
hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL);
}
//começar o cronometro, ativar as threadas
QueryPerformanceCounter(&inicio);
for (i = 0; i < numeroThreads; i++) //dividir os intervalos
{
ResumeThread(hThreads[i]); //ativar as threads
}
WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem
//esperar que as threads terminem, ler o cronometro
QueryPerformanceCounter(&fim);
duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart;
_tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao);
for (i = 0; i < numeroThreads; i++)
{
CloseHandle(hThreads[i]);
}
//fechar o mutex
CloseHandle(mutex);
//agora os resultados já nao vão variar, vão ser sempre os corretos
//o tempo de execução aumenta
return 0;
}
critical section: recurso partilhado (em vez de mutexes)
mecanismo de controlo de acesso: CriticalSections, espécie de mutex, que tambem controla o acesso à secção critica
é uma espera ativa, que vai verificar um determinado numero de vezes se pode entrar
criticalsection é como um mini-mutex optimizado para threads dentro do mesmo processo
a thread tem um periodo de espera ativa à entrada da criticalsection, em que vai consultado para ver se pode entrar um numero de vezes (no max spin count)
pode aliviar o esforço de sinalização entre threads principalmente para spin count baixos
eficicente quando o recurso fica bloqueado por períodos curtos: assim não é preciso suspender e depois sinalizar threads tantas vezes
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#define MAX_THREADS 20
typedef struct {
unsigned int limiteBaixo;
unsigned int limiteAlto;
unsigned int* contadorPartilhado;
//referencia da criticalSection
CRITICAL_SECTION * cirticalSectionPartilhada;
} TDados;
DWORD WINAPI ContaMultiplosTres(LPVOID lmParam)
{
unsigned int i;
TDados* data = (TDados*)lmParam;
for (i = data->limiteBaixo; i <= data->limiteAlto; i++)
{
if (i % 3 == 0)
{
//entrada na critical section
EnterCriticalSection(data->cirticalSectionPartilhada);
(*(data->contadorPartilhado))++;
//saida na critical section
LeaveCriticalSection(data->cirticalSectionPartilhada);
}
}
return 0;
}
int _tmain(int argc, TCHAR* argv[])
{
HANDLE hThreads[MAX_THREADS];
TDados tdados[MAX_THREADS];
unsigned int numeroThreads, i, limSuperior = 4000000, contador = 0;
LARGE_INTEGER tickSegundo, inicio, fim;
double duracao;
//declaração variavel critical section
CRITICAL_SECTION criticalSection;
#ifdef UNICODE
_setmode(_fileno(stdin), _O_WTEXT);
_setmode(_fileno(stdout), _O_WTEXT);
_setmode(_fileno(stderr), _O_WTEXT);
#endif
if (!QueryPerformanceCounter(&tickSegundo))
{
_tprintf(TEXT("Erro: obter ticks por segundo.\n"));
}
_tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS);
_tscanf_s(TEXT("%u"), &numeroThreads);
//inicializar da criticalsection
if(!InitializeCriticalSectionAndSpinCount(&criticalSection, 400)) //spinout 400, normal..
{
return 0;
}
//lançar as threads (suspensas)
for (i = 0; i < numeroThreads; i++)
{
tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i;
tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1);
tdados[i].contadorPartilhado = &contador;
//passar o ponteiro da critical section
tdados[i].cirticalSectionPartilhada = &criticalSection;
_tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto);
hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL);
}
//começar o cronometro, ativar as threadas
QueryPerformanceCounter(&inicio);
for (i = 0; i < numeroThreads; i++) //dividir os intervalos
{
ResumeThread(hThreads[i]); //ativar as threads
}
WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem
//esperar que as threads terminem, ler o cronometro
QueryPerformanceCounter(&fim);
duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart;
_tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao);
for (i = 0; i < numeroThreads; i++)
{
CloseHandle(hThreads[i]);
}
//remover a criticalsection
DeleteCriticalSection(&criticalSection);
//melhoria de desempenho muito consideravel
//
return 0;
}
eventos: atuam como flags para assinalar à thread se pode continuar ou não
podem ser usados para outros cenários que não threads
a usar: CreateEventA, SetEvent
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#define MAX_THREADS 20
typedef struct {
unsigned int limiteBaixo;
unsigned int limiteAlto;
unsigned int* contadorPartilhado;
CRITICAL_SECTION* cirticalSectionPartilhada;
//referência a evento
HANDLE eventoPartilhado;
} TDados;
DWORD WINAPI ContaMultiplosTres(LPVOID lmParam)
{
unsigned int i;
TDados* data = (TDados*)lmParam;
//aguardar evento antes de começar, a thread fica à espera que o evento seja TRUE
WaitForSingleObject(data->eventoPartilhado, INFINITE);
for (i = data->limiteBaixo; i <= data->limiteAlto; i++)
{
if (i % 3 == 0)
{
EnterCriticalSection(data->cirticalSectionPartilhada);
(*(data->contadorPartilhado))++;
LeaveCriticalSection(data->cirticalSectionPartilhada);
}
}
return 0;
}
int _tmain(int argc, TCHAR* argv[])
{
HANDLE hThreads[MAX_THREADS];
TDados tdados[MAX_THREADS];
unsigned int numeroThreads, i, limSuperior = 4000000, contador = 0;
LARGE_INTEGER tickSegundo, inicio, fim;
double duracao;
CRITICAL_SECTION criticalSection;
//evento
HANDLE evento;
#ifdef UNICODE
_setmode(_fileno(stdin), _O_WTEXT);
_setmode(_fileno(stdout), _O_WTEXT);
_setmode(_fileno(stderr), _O_WTEXT);
#endif
if (!QueryPerformanceCounter(&tickSegundo))
{
_tprintf(TEXT("Erro: obter ticks por segundo.\n"));
}
_tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS);
_tscanf_s(TEXT("%u"), &numeroThreads);
if (!InitializeCriticalSectionAndSpinCount(&criticalSection, 400)) //spinout 400, normal..
{
return 0;
}
//criar o evento
evento = CreateEvent(NULL, TRUE, FALSE, NULL);
for (i = 0; i < numeroThreads; i++)
{
tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i;
tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1);
tdados[i].contadorPartilhado = &contador;
tdados[i].cirticalSectionPartilhada = &criticalSection;
//passar o evento para a thread
tdados[i].eventoPartilhado = evento;
_tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto);
hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL);
}
QueryPerformanceCounter(&inicio);
//gerar evento para threads avançarem
SetEvent(evento);
//o ResumeThread já não é necessário
WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem
QueryPerformanceCounter(&fim);
duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart;
_tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao);
for (i = 0; i < numeroThreads; i++)
{
CloseHandle(hThreads[i]);
}
DeleteCriticalSection(&criticalSection);
//termina a o evento
CloseHandle(evento);
//as threds começam todas ao mesmo tempo, o tempo vai demorar
//este mecanismo permite desbloquear um processo
return 0;
}
0 thoughts on “sessão 5 – Gestão básica de threads em Win32”