Sobes.tech
Middle

Como pode sincronizar a interação entre threads?

sobes.tech IA

Resposta da IA

Existem vários mecanismos principais para sincronização e interação entre threads em C/C++:

  • Mutexes (Mutual Exclusion): Garantem acesso exclusivo a um recurso partilhado. O thread que adquire o mutex bloqueia os outros até o libertar.

    #include <mutex>
    
    std::mutex my_mutex;
    
    void my_thread_function() {
        my_mutex.lock(); // Adquirir o mutex
        // Trabalhar com o recurso partilhado
        my_mutex.unlock(); // Libertar o mutex
    }
    

    Ou usando RAII:

    #include <mutex>
    #include <lock_guard>
    
    std::mutex my_mutex;
    
    void my_thread_function() {
        std::lock_guard<std::mutex> lock(my_mutex); // Adquirir o mutex ao criar o objeto
        // Trabalhar com o recurso partilhado
        // O mutex é libertado automaticamente ao sair do escopo
    }
    
  • Semáforos (Semaphores): Generalização dos mutexes. Mantêm um contador que permite a um número definido de threads acederem simultaneamente ao recurso.

    #include <semaphore.h> // Para semáforos POSIX
    #include <windows.h> // Para semáforos Windows
    
    sem_t my_semaphore; // Semáforo POSIX
    
    void my_thread_function() {
        sem_wait(&my_semaphore); // Diminuir o contador do semáforo, bloquear se for 0
        // Trabalhar com o recurso
        sem_post(&my_semaphore); // Aumentar o contador do semáforo
    }
    

    (Nota: A norma C++ não inclui semáforos diretamente, mas estão disponíveis através de bibliotecas do sistema ou implementações de terceiros.)

  • Variáveis de condição (Condition Variables): São usadas para que um thread espere que uma condição específica seja satisfeita, libertando o mutex nesse processo. Outro thread pode notificar que a condição foi cumprida.

    #include <mutex>
    #include <condition_variable>
    
    std::mutex my_mutex;
    std::condition_variable my_cv;
    bool condition_met = false;
    
    void waiting_thread() {
        std::unique_lock<std::mutex> lock(my_mutex);
        my_cv.wait(lock, []{ return condition_met; }); // Esperar até a condição ser satisfeita
        // A condição foi satisfeita, trabalhar com o recurso
    }
    
    void notifying_thread() {
        std::unique_lock<std::mutex> lock(my_mutex);
        condition_met = true;
        lock.unlock(); // Libertar o mutex antes de notificar (opcional, mas recomendado)
        my_cv.notify_one(); // Notificar um thread em espera
        // ou
        // my_cv.notify_all(); // Notificar todos os threads em espera
    }
    
  • Operações atômicas (Atomic Operations): Permitem realizar operações simples sobre variáveis (por exemplo, incremento, decremento) como uma ação indivisível, sem necessidade de bloqueio explícito com um mutex.

    #include <atomic>
    
    std::atomic<int> counter(0);
    
    void my_thread_function() {
        counter++; // Incremento atômico
        // Ou:
        // counter.fetch_add(1);
    }
    
  • Bloques de leitura-escrita (Shared Mutexes): Permitem que vários threads leiam dados simultaneamente, mas apenas um pode escrever.

    #include <shared_mutex> // C++17 e superior
    
    std::shared_mutex my_shared_mutex;
    
    void read_thread() {
        my_shared_mutex.lock_shared(); // Adquirir para leitura
        // Ler dados
        my_shared_mutex.unlock_shared(); // Libertar para leitura
    }
    
    void write_thread() {
        my_shared_mutex.lock(); // Adquirir para escrita (exclusivo)
        // Escrever dados
        my_shared_mutex.unlock(); // Libertar para escrita
    }
    
  • Barreiras: Permitem que um grupo de threads espere até que todos atinjam um ponto determinado na sua execução, antes de continuar.

    #include <barrier> // C++20
    
    std::barrier sync_point(4); // Barreira para 4 threads
    
    void my_thread_function() {
        // Trabalho antes da barreira
        sync_point.arrive_and_wait(); // Esperar até que todos atinjam a barreira
        // Trabalho após a barreira
    }
    

A escolha de um mecanismo específico depende da natureza da interação e dos recursos partilhados.