Como pode sincronizar a interação entre threads?
sobes.tech IA
Resposta da IA
Existem vários mecanismos principais de sincronização de threads em C++:
-
Mutexes: Garantem exclusão mútua. Apenas um thread pode possuir um mutex de cada vez. São usados para proteger recursos partilhados contra acessos simultâneos.
#include <mutex> std::mutex myMutex; void criticalSection() { std::lock_guard<std::mutex> lock(myMutex); // Aquisição do mutex // Trabalho com recurso partilhado } // O mutex é libertado automaticamente ao sair do escopo -
Semáforos: Controlam o acesso a recursos limitados. Têm um contador que indica o número de recursos disponíveis. Os threads podem diminuir (para obter um recurso) ou aumentar (para libertar).
#include <semaphore.h> // Geralmente usado em sistemas POSIX sem_t mySemaphore; void initSemaphore(int count) { sem_init(&mySemaphore, 0, count); // Inicialização do semáforo com o contador count } void acquireResource() { sem_wait(&mySemaphore); // Diminuição do contador, bloqueio se for zero // Uso do recurso } void releaseResource() { sem_post(&mySemaphore); // Aumento do contador } -
Variáveis de condição: Permitem que os threads esperem por uma condição específica. Geralmente são usadas juntamente com mutexes. Um thread que espera liberta o mutex e fica bloqueado até que outro thread o notifique de que a condição foi satisfeita.
#include <condition_variable> #include <mutex> #include <queue> std::condition_variable myCondition; std::mutex myMutex; std::queue<int> myQueue; void producer(int value) { std::lock_guard<std::mutex> lock(myMutex); myQueue.push(value); myCondition.notify_one(); // Notificação de um thread em espera } int consumer() { std::unique_lock<std::mutex> lock(myMutex); myCondition.wait(lock, [&]{ return !myQueue.empty(); }); // Espera pela condição int value = myQueue.front(); myQueue.pop(); return value; } -
Variáveis atômicas: Permitem realizar operações simples (leitura, escrita, modificação) de forma atômica, sem necessidade de mutexes para essas operações específicas.
#include <atomic> std::atomic_int atomicCounter(0); void incrementCounter() { atomicCounter++; // Incremento atômico } -
Barreiras: Sincronizam vários threads para que nenhum possa continuar até que todos tenham atingido a barreira.
#include <barrier> // C++20 std::barrier myBarrier(3); // Barreira para 3 threads void workerThread() { // Execução de parte do trabalho myBarrier.arrive_and_wait(); // Espera que todos cheguem à barreira // Execução da próxima parte do trabalho }
A escolha de um mecanismo específico depende da natureza da interação entre os threads:
- Mutexes: A forma mais comum de proteger dados partilhados.
- Semáforos: Gestão do acesso a recursos limitados.
- Variáveis de condição: Espera que um evento ou estado específico aconteça.
- Variáveis atômicas: Execução eficiente de operações simples e atômicas.
- Barreiras: Sincronização de threads para avançar conjuntamente para a próxima etapa.