#include #include #include #include #include /* Inicializacao estatica. NP significa Non-portable Para usar este inicializador, devemos compilar o programa informando a flag -D_GNU_SOURCE */ pthread_mutex_t lock = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; pthread_barrier_t barrier; void* f_thread1(void *v) { int cont = 0; long i = 0; /* Mutexes do tipo Adaptivo aplicam-se somente a computadores multiprocessados. Neste tipo de mutex, uma thread quando não consegue obter o lock continua em "spin", executando um while onde a cada iteração tenta-se novamente a obtenção do lock. A cada chamada a pthread_mutex_lock, o número de vezes que uma thread itera é corrigido a fim de convergir para o número correto de iterações necessárias para obter o lock (daí vem o nome adaptivo). Este número de "spins" é mantido na estrutura interna do mutex (campo __data.__spins). É importante ressaltar que caso a thread não obtenha o lock nos spins ela é colocada para dormir, assim como acontece com os mutexes normais. A idéia por trás deste mutex é evitar chamadas desnecessárias ao sleep, que faz com que a thread seja retirada do contexto de execução. Além disso, quando uma thread obtém o lock e é acordada, ela é novamente colocada no contexto. Operações de troca de contexto são caras; se a thread consegue o lock antes de ir dormir evitam-se estas operações. A desvantagem deste tipo de mutex é que a thread se mantem por um tempo em um estado de "busy-wait", gastando ciclos de CPUs desnecessariamente. Este programa tenta ilustrar estes conceitos fazendo com que as threads compitam pelo mutex por 50 vezes e imprimindo o número de spins determinado a cada iteração. A região crítica faz alguns cálculos para que o mutex seja mantido por uma das threads por um tempo mínimo e a outra tenha que aguardar. Em nossas máquinas, a cada execução deste programa o número de spins variou bastante, mostrando a capacidade de adaptação do mutex. */ for (cont = 0; cont < 50; cont++) { pthread_mutex_lock(&lock); printf("Thread 1 - Lock obtido.\n"); for (i = 0; i < 10000; i++) { int k = i*i*i; } printf("Thread 1 - Numero de spins: %d.\n", lock.__data.__spins); pthread_mutex_unlock(&lock); /* A barreira é utilizada para que na próxima iteração ambas threads concorram novamente pelo mutex. */ pthread_barrier_wait(&barrier); } return NULL; } void* f_thread2(void *v) { int cont = 0; long i = 0; for (cont = 0; cont < 50; cont++) { pthread_mutex_lock(&lock); printf("Thread 2 - Lock obtido.\n"); for (i = 0; i < 10000; i++) { int k = i*i*i; /* sqrt(i); */ } printf("Thread 2 - Numero de spins: %d.\n", lock.__data.__spins); pthread_mutex_unlock(&lock); pthread_barrier_wait(&barrier); } return NULL; } int main() { pthread_t thr1, thr2; pthread_barrier_init(&barrier, NULL, 2); pthread_create(&thr1, NULL, f_thread1, NULL); pthread_create(&thr2, NULL, f_thread2, NULL); pthread_join(thr1, NULL); pthread_join(thr2, NULL); pthread_barrier_destroy(&barrier); printf("Finalizando programa.\n"); return 0; }