Suche…


Einführung

Die Arbeit mit Threads erfordert möglicherweise einige Synchronisationstechniken, wenn die Threads interagieren. In diesem Thema finden Sie die verschiedenen Strukturen, die von der Standardbibliothek bereitgestellt werden, um diese Probleme zu lösen.

std :: shared_lock

Ein shared_lock kann zusammen mit einer eindeutigen Sperre verwendet werden, um mehrere Leser und exklusive Schreiber zuzulassen.

#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 stellt die Ausführung einer Funktion durch konkurrierende Threads genau einmal sicher. Es wirft std::system_error aus, falls es seine Aufgabe nicht erfüllen kann.

Wird zusammen mit 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;
}

Objektverriegelung für einen effizienten Zugriff.

Oft möchten Sie das gesamte Objekt sperren, während Sie mehrere Vorgänge daran ausführen. Zum Beispiel, wenn Sie das Objekt mithilfe von Iteratoren untersuchen oder ändern müssen. Wenn Sie mehrere Elementfunktionen aufrufen müssen, ist es generell effizienter, das gesamte Objekt als einzelne Elementfunktionen zu sperren.

Zum Beispiel:

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
};

Bei der Berechnung einer Prüfsumme wird das Objekt zum Lesen gesperrt, sodass andere Threads, die gleichzeitig aus dem Objekt lesen möchten, dies tun können.

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;
}

Durch das Löschen des Objekts werden seine internen Daten aktualisiert, sodass eine Ausschließungssperre erforderlich ist.

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

Wenn Sie mehr als eine Sperre erhalten, müssen Sie darauf achten, dass die Sperren für alle Threads immer in derselben Reihenfolge abgerufen werden.

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));
}

Anmerkung: Dies geschieht am besten mit std :: deferred :: lock und dem Aufruf von std :: lock

std :: condition_variable_any, std :: cv_status

Eine Verallgemeinerung von std::condition_variable , std::condition_variable_any funktioniert mit jeder Art von BasicLockable-Struktur.

std::cv_status als Rückgabestatus für eine Bedingungsvariable hat zwei mögliche Rückgabecodes:

  • std :: cv_status :: no_timeout: Es gab kein Timeout, die Zustandsvariable wurde benachrichtigt
  • std :: cv_status :: no_timeout: Zeitlimit für Zustandsvariable


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow