Recherche…


Introduction

Travailler avec des threads peut nécessiter des techniques de synchronisation si les threads interagissent. Dans cette rubrique, vous pouvez trouver les différentes structures fournies par la bibliothèque standard pour résoudre ces problèmes.

std :: shared_lock

Un shared_lock peut être utilisé conjointement avec un verrou unique pour autoriser plusieurs lecteurs et écrivains exclusifs.

#include <unordered_map>
#include <mutex>
#include <shared_mutex>
#include <thread>
#include <string>
#include <iostream>
    
class PhoneBook {
    public:
        string getPhoneNo( const std::string & name )
        {
            shared_lock<shared_timed_mutex> r(_protect);
            auto it =  _phonebook.find( name );
            if ( it == _phonebook.end() )
                return (*it).second;
            return "";
        }
        void addPhoneNo ( const std::string & name, const std::string & phone )
        {
            unique_lock<shared_timed_mutex> w(_protect);
            _phonebook[name] = phone;
        }
        
        shared_timed_mutex _protect;
        unordered_map<string,string>  _phonebook;
    };

std :: call_once, std :: once_flag

std::call_once assure l'exécution d'une fonction exactement une fois par des threads concurrents. Il jette std::system_error au cas où il ne pourrait pas terminer sa tâche.

Utilisé conjointement avec s td::once_flag .

#include <mutex>
#include <iostream>

std::once_flag flag;
void do_something(){
      std::call_once(flag, [](){std::cout << "Happens once" << std::endl;});
    
      std::cout << "Happens every time" << std::endl;
}

Verrouillage d'objet pour un accès efficace.

Vous voulez souvent verrouiller l’objet entier pendant que vous effectuez plusieurs opérations sur celui-ci. Par exemple, si vous devez examiner ou modifier l'objet à l'aide d' itérateurs . Chaque fois que vous avez besoin d'appeler plusieurs fonctions membres, il est généralement plus efficace de verrouiller l'objet entier plutôt que les fonctions membres individuelles.

Par exemple:

class text_buffer
{
    // for readability/maintainability
    using mutex_type = std::shared_timed_mutex;
    using reading_lock = std::shared_lock<mutex_type>;
    using updates_lock = std::unique_lock<mutex_type>;

public:
    // This returns a scoped lock that can be shared by multiple
    // readers at the same time while excluding any writers
    [[nodiscard]]
    reading_lock lock_for_reading() const { return reading_lock(mtx); }

    // This returns a scoped lock that is exclusing to one
    // writer preventing any readers
    [[nodiscard]]
    updates_lock lock_for_updates() { return updates_lock(mtx); }

    char* data() { return buf; }
    char const* data() const { return buf; }

    char* begin() { return buf; }
    char const* begin() const { return buf; }

    char* end() { return buf + sizeof(buf); }
    char const* end() const { return buf + sizeof(buf); }

    std::size_t size() const { return sizeof(buf); }

private:
    char buf[1024];
    mutable mutex_type mtx; // mutable allows const objects to be locked
};

Lors du calcul d'une somme de contrôle, l'objet est verrouillé pour la lecture, permettant ainsi aux autres threads qui veulent lire à partir de l'objet en même temps.

std::size_t checksum(text_buffer const& buf)
{
    std::size_t sum = 0xA44944A4;

    // lock the object for reading
    auto lock = buf.lock_for_reading();

    for(auto c: buf)
        sum = (sum << 8) | (((unsigned char) ((sum & 0xFF000000) >> 24)) ^ c);

    return sum;
}

L'effacement de l'objet met à jour ses données internes. Vous devez donc utiliser un verrou exclusif.

void clear(text_buffer& buf)
{
    auto lock = buf.lock_for_updates(); // exclusive lock
    std::fill(std::begin(buf), std::end(buf), '\0');
}

Lors de l'obtention de plusieurs verrous, veillez à toujours acquérir les verrous dans le même ordre pour tous les threads.

void transfer(text_buffer const& input, text_buffer& output)
{
    auto lock1 = input.lock_for_reading();
    auto lock2 = output.lock_for_updates();

    std::copy(std::begin(input), std::end(input), std::begin(output));
}

note: il vaut mieux utiliser std :: deferred :: lock et appeler std :: lock

std :: condition_variable_any, std :: cv_status

Une généralisation de std::condition_variable , std::condition_variable_any fonctionne avec tout type de structure BasicLockable.

std::cv_status comme statut de retour pour une variable de condition a deux codes de retour possibles:

  • std :: cv_status :: no_timeout: Il n'y avait pas de délai d'attente, la variable de condition a été notifiée
  • std :: cv_status :: no_timeout: La variable de condition a expiré


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow