std:: scoped_lock
|
Défini dans l'en-tête
<mutex>
|
||
|
template
<
class
...
MutexTypes
>
class scoped_lock ; |
(depuis C++17) | |
La classe
scoped_lock
est un wrapper de mutex qui fournit un mécanisme pratique de type
RAII
pour posséder zéro ou plusieurs mutex pendant la durée d'un bloc de portée.
Lorsqu'un objet
scoped_lock
est créé, il tente de prendre la possession des mutex qui lui sont donnés. Lorsque le contrôle quitte la portée dans laquelle l'objet
scoped_lock
a été créé, le
scoped_lock
est détruit et les mutex sont libérés. Si plusieurs mutex sont donnés, l'algorithme d'évitement des interblocages est utilisé comme par
std::lock
.
La classe
scoped_lock
n'est pas copiable.
Table des matières |
Paramètres du modèle
| MutexTypes | - | les types des mutex à verrouiller. Les types doivent satisfaire aux Lockable exigences, sauf si sizeof... ( MutexTypes ) == 1 , auquel cas le seul type doit satisfaire aux BasicLockable |
Types membres
| Type de membre | Définition |
mutex_type
(conditionnellement présent) |
Si
sizeof...
(
MutexTypes
)
==
1
, le type de membre
|
Fonctions membres
construit un
scoped_lock
, verrouillant optionnellement les mutex donnés
(fonction membre publique) |
|
détruit l'objet
scoped_lock
, déverrouille les mutex sous-jacents
(fonction membre publique) |
|
|
operator=
[deleted]
|
non assignable par copie
(fonction membre publique) |
Notes
Une erreur courante chez les débutants est d'« oublier » de donner un nom à une variable
scoped_lock
, par exemple
std
::
scoped_lock
(
mtx
)
;
(qui construit par défaut une variable
scoped_lock
nommée
mtx
) ou
std
::
scoped_lock
{
mtx
}
;
(qui construit un objet prvalue immédiatement détruit), ne construisant ainsi pas réellement un verrou qui maintient un mutex pour le reste de la portée.
| Macro de test de fonctionnalité | Valeur | Norme | Fonctionnalité |
|---|---|---|---|
__cpp_lib_scoped_lock
|
201703L
|
(C++17) |
std::scoped_lock
|
Exemple
L'exemple suivant utilise
std::scoped_lock
pour verrouiller des paires de mutex sans interblocage et suit le style RAII.
#include <chrono> #include <functional> #include <iostream> #include <mutex> #include <string> #include <syncstream> #include <thread> #include <vector> using namespace std::chrono_literals; struct Employee { std::vector<std::string> lunch_partners; std::string id; std::mutex m; Employee(std::string id) : id(id) {} std::string partners() const { std::string ret = "Employé " + id + " a des partenaires de déjeuner: "; for (int count{}; const auto& partner : lunch_partners) ret += (count++ ? ", " : "") + partner; return ret; } }; void send_mail(Employee&, Employee&) { // Simuler une opération de messagerie longue std::this_thread::sleep_for(1s); } void assign_lunch_partner(Employee& e1, Employee& e2) { std::osyncstream synced_out(std::cout); synced_out << e1.id << " et " << e2.id << " attendent des verrous" << std::endl; { // Utilisez std::scoped_lock pour acquérir deux verrous sans vous soucier de // autres appels à assign_lunch_partner nous bloquant // et il fournit également un mécanisme pratique de style RAII std::scoped_lock lock(e1.m, e2.m); // Code équivalent 1 (utilisant std::lock et std::lock_guard) // std::lock(e1.m, e2.m); // std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock); // std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock); // Code équivalent 2 (si des verrous uniques sont nécessaires, par exemple pour des variables de condition) // std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock); // std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock); // std::lock(lk1, lk2); synced_out << e1.id << " et " << e2.id << " a obtenu des verrous" << std::endl; e1.lunch_partners.push_back(e2.id); e2.lunch_partners.push_back(e1.id); } send_mail(e1, e2); send_mail(e2, e1); } int main() { Employee alice("Alice"), bob("Bob"), christina("Christina"), dave("Dave"); // Assigner en parallèle dans des threads car envoi d'emails aux utilisateurs concernant les affectations de déjeuner // prend beaucoup de temps std::vector<std::thread> threads; threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice)); threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob)); for (auto& thread : threads) thread.join(); std::osyncstream(std::cout) << alice.partenaires() << '\n' << bob.partenaires() << '\n' << christina.partenaires() << '\n' << dave.partenaires() << '\n'; }
Sortie possible :
Alice et Bob attendent des verrous Alice et Bob ont obtenu des verrous Christina et Bob attendent des verrous Christina et Alice attendent des verrous Dave et Bob attendent des verrous Dave et Bob ont obtenu des verrous Christina et Alice ont obtenu des verrous Christina et Bob ont obtenu des verrous Employé Alice a des partenaires de déjeuner : Bob, Christina Employé Bob a des partenaires de déjeuner : Alice, Dave, Christina Employé Christina a des partenaires de déjeuner : Alice, Bob Employé Dave a des partenaires de déjeuner : Bob
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | S'applique à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| LWG 2981 | C++17 |
le guide de déduction redondant de
scoped_lock<MutexTypes...>
était fourni
|
supprimé |
Voir aussi
|
(C++11)
|
implémente un wrapper de propriété de mutex déplaçable
(modèle de classe) |
|
(C++11)
|
implémente un wrapper de propriété de mutex strictement basé sur la portée
(modèle de classe) |