Sobes.tech
Middle

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.