C++-Standardbibliothek<mutex>

In der Multithread-Programmierung ist die Gewährleistung von Datenkonsistenz und Thread-Sicherheit entscheidend.

Die C++-Standardbibliothek bietet eine reichhaltige Sammlung von Synchronisierungsprimitiven, die verwendet werden, um den Zugriff auf gemeinsam genutzte Ressourcen zu steuern.

In der C++-Standardbibliothek,<mutex>Die Header-Datei bietet eine Reihe von Werkzeugen zur Implementierung von Synchronisation und gegenseitigem Ausschluss zwischen Threads in Multithread-Programmen.

<mutex>Die Header-Datei wurde in C++11 eingeführt und enthält Klassen und Funktionen für Mutex-Sperren. Eine Mutex-Sperre ist ein Synchronisierungsmechanismus, der verwendet wird, um zu verhindern, dass mehrere Threads gleichzeitig auf gemeinsam genutzte Ressourcen zugreifen.

Ein Mutex ist ein Synchronisierungsprimitiv, das verwendet wird, um den Zugriff auf gemeinsam genutzte Ressourcen zu steuern. Wenn ein Thread auf eine gemeinsam genutzte Ressource zugreifen muss, versucht er, den Mutex zu sperren. Wenn der Mutex bereits von einem anderen Thread gesperrt wurde, wird der anfordernde Thread blockiert, bis der Mutex freigegeben wird.

Grundlegende Syntax

In C++,<mutex>Die Header-Datei bietet die folgenden Hauptklassen:

  • std::mutex: Einfacher Mutex.
  • std::recursive_mutex: Rekursive Mutex-Sperre, die es demselben Thread ermöglicht, mehrfach zu sperren.
  • std::timed_mutex: Mutex mit Timeout-Funktionalität.
  • std::recursive_timed_mutex: Rekursiver Mutex mit Timeout-Funktionalität.

Beispiel

1. Verwendungstd::mutex

Das Folgende ist ein einfaches Beispiel, das zeigt, wie man es in C++ verwendetstd::mutexum den Zugriff auf gemeinsam genutzte Ressourcen zu synchronisieren.

Beispiel

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx; // Globale Mutex-Sperre
int shared_resource = 0;

void increment() {
    for (int i = 0; i < 10000; ++i) {
        mtx.lock(); // Mutex sperren
        ++shared_resource;
        mtx.unlock(); // Mutex entsperren
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Endgültiger Wert von shared_resource: " << shared_resource << std::endl;
    return 0;
}

Ausgabeergebnis:

Final value of shared_resource: 20000

2. Verwendungstd::recursive_mutex

Ein rekursiver Mutex-Lock ermöglicht es demselben Thread, denselben Mutex mehrmals zu sperren. Das Folgende ist ein Beispiel unter Verwendung vonstd::recursive_mutexBeispiel.

Beispiel

#include <iostream>
#include <thread>
#include <mutex>

std::recursive_mutex rmtx; // Rekursives Mutex-Objekt erstellen
int shared_resource = 0; // Gemeinsam genutzte Ressource

// Rekursive Funktion
void recursive_increment(int count) {
    if (count <= 0) return;

    std::lock_guard<std::recursive_mutex> lock(rmtx); // Sperren, um Thread-Sicherheit zu gewährleisten
    ++shared_resource;
    std::cout << "shared_resource inkrementiert auf " << shared_resource << " (count = " << count << ")" << std::endl;

    // Rekursiver Aufruf
    recursive_increment(count - 1);
}

int main() {
    std::thread t1(recursive_increment, 3); // Thread t1 führt recursive_increment(3) aus
    std::thread t2(recursive_increment, 3); // Thread t2 führt recursive_increment(3) aus
   
    t1.join(); // Warten, bis Thread t1 beendet ist
    t2.join(); // Warten, bis Thread t2 beendet ist

    std::cout << "Endgültiger Wert von shared_resource: " << shared_resource << std::endl;

    return 0;
}

Ausgabeergebnis:

Incremented shared_resource to 1 (count = 3)
Incremented shared_resource to 2 (count = 2)
Incremented shared_resource to 3 (count = 1)
Incremented shared_resource to 4 (count = 0)
Incremented shared_resource to 5 (count = 3)
Incremented shared_resource to 6 (count = 2)
Incremented shared_resource to 7 (count = 1)
Incremented shared_resource to 8 (count = 0)
Final value of shared_resource: 8

Codeanalyse:

  1. Erstellestd::recursive_mutexObjekt:std::recursive_mutex rmtx;Ist ein rekursiver Mutex, der es demselben Thread ermöglicht, die Sperre mehrfach zu erwerben.

  2. Gemeinsam genutzte Ressource:int shared_resource = 0;ist eine Ressource, auf die mehrere Threads gleichzeitig zugreifen.

  3. Rekursive Funktionrecursive_increment:

    • Nutzungstd::lock_guard<std::recursive_mutex>PairrmtxSperren. Da ein rekursiver Mutex verwendet wird, kann derselbe Thread die Sperre mehrmals erwerben.
    • Durch++shared_resourceErhöht den Wert der gemeinsam genutzten Ressource.
    • Die Funktion ruft sich selbst rekursiv auf und veranschaulicht, wie eine rekursive Funktion sicher funktioniert, wenn die Sperre mehrmals erworben wird.
  4. Erstellen und Ausführen von Threads:

    • std::thread t1(recursive_increment, 3);undstd::thread t2(recursive_increment, 3);Zwei Threads werden separat erstellt, und beide werden ausgeführtrecursive_increment(3)。
    • t1.join()undt2.join()Wartet darauf, dass zwei Threads ihre Ausführung abschließen.

Hinweise

  • Stellen Sie sicher, dass nach jedem Erwerb einer Mutex-Sperre eine Entsperroperation ausgeführt wird, um Deadlocks zu vermeiden.
  • Nutzungstd::lock_guardoderstd::unique_lockKann das Sperren und Entsperren von Mutex-Sperren automatisch verwalten, wodurch die Wahrscheinlichkeit von Fehlern verringert wird.
  • Vermeiden Sie den Aufruf von Funktionen, die Ausnahmen auslösen können, während eine Mutex-Sperre gehalten wird, da dies zu einem Deadlock führen kann.

<mutex>Ist eine sehr wichtige Header-Datei in der C++-Standardbibliothek und bietet grundlegende Synchronisationsmechanismen für die Multithread-Programmierung. Durch die Verwendung von Mutex-Sperren können wir sicherstellen, dass der Zugriff auf gemeinsam genutzte Ressourcen sicher ist, wodurch Datenrennen und Inkonsistenzprobleme vermieden werden.


Weitere Informationen

1. std::mutex

Bietet einen grundlegenden Mutex, um sicherzustellen, dass nur ein Thread gleichzeitig auf gemeinsam genutzte Ressourcen zugreifen kann.

Beispiel

#include <mutex>

std::mutex mtx;

void thread_function() {
    std::lock_guard<std::mutex> lock(mtx);
    // Zugriff auf gemeinsame Ressource
}

2. std::recursive_mutex

Ermöglicht demselben Thread, die Sperre mehrmals zu erwerben, ohne einen Deadlock zu verursachen, was besonders nützlich für rekursive Funktionen ist.

Beispiel

#include <mutex>

std::recursive_mutex rmtx;

void recursive_function(int count) {
    if (count <= 0) return;
    std::lock_guard<std::recursive_mutex> lock(rmtx);
    // Rekursiver Aufruf
    recursive_function(count - 1);
}

3. std::timed_mutex

Stellt einen zeitgesteuerten Mutex bereit, der beim Versuch, die Sperre zu erwerben, eine Zeitüberschreitung festlegen kann.

Beispiel

#include <mutex>
#include <chrono>

std::timed_mutex tm;

void try_lock_for_example() {
    if (tm.try_lock_for(std::chrono::seconds(1))) {
        // Sperre erfolgreich erworben
        tm.unlock();
    } else {
        // Sperre konnte nicht erworben werden
    }
}

4. std::recursive_timed_mutex

Von std::timed_mutex geerbt, wodurch derselbe Thread die Sperre mehrmals erwerben kann und gleichzeitig zeitgesteuerte Funktionalität unterstützt wird.

Beispiel

#include <mutex>
#include <chrono>

std::recursive_timed_mutex rtm;

void recursive_timed_function(int count) {
    if (count <= 0) return;
    if (rtm.try_lock_for(std::chrono::seconds(1))) {
        // Sperre erfolgreich erworben
        recursive_timed_function(count - 1);
        rtm.unlock();
    } else {
        // Sperre konnte nicht erworben werden
    }
}

5. std::lock_guard

Ein Wrapper, der std::mutex-Sperren automatisch verwaltet und dabei den RAII-Stil verwendet, um sicherzustellen, dass die Sperre automatisch freigegeben wird, wenn der Gültigkeitsbereich endet.

Beispiel

#include <mutex>

std::mutex mtx;

void function() {
    std::lock_guard<std::mutex> lock(mtx);
    // Zugriff auf gemeinsame Ressource
}

6. std::unique_lock

Bietet eine flexiblere Sperrenverwaltung als std::lock_guard, ermöglicht manuelles Freigeben und erneutes Erwerben von Sperren und unterstützt außerdem zeitgesteuertes Sperren.

Beispiel

#include <mutex>
#include <chrono>

std::mutex mtx;

void function() {
    std::unique_lock<std::mutex> lock(mtx);
    // Zugriff auf gemeinsame Ressource
   
    // Sperre kann manuell freigegeben werden
    lock.unlock();
   
    // Sperre kann erneut erworben werden
    lock.lock();
   
    // Zeitgesteuertes Sperren möglich
    if (lock.try_lock_for(std::chrono::seconds(1))) {
        // Sperre erfolgreich erworben
    }
}

7. std::adopt_lock_t

Ein Flag-Typ, der verwendet wird, um anzugeben, dass std::unique_lock eine vorhandene Sperre übernimmt.

Beispiel

std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx, std::adopt_lock);

8. std::defer_lock_t

Funktion: Ein Flag-Typ, der für verzögertes Sperren verwendet wird; während der Initialisierung wird keine Sperre erworben.

Beispiel

std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
// lock.lock() kann zu einem späteren Zeitpunkt aufgerufen werden

9. std::try_to_lock_t

Flag-Typ, der verwendet wird, um ohne Blockieren eine Sperre zu versuchen.

Beispiel

std::mutex mtx1, mtx2;
std::unique_lock<std::mutex> lock1(mtx1, std::try_to_lock);
std::unique_lock<std::mutex> lock2(mtx2, std::try_to_lock);

if (lock1 && lock2) {
    // Beide Sperren erfolgreich erworben
}
Weitere Erweiterungen