Tag: SO2 – 2021 – Ficha 5 v.2.4 – Sincronizacao I.pdf

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;
}
Tags : , , ,