Różnica polega na tym, że możesz zablokować i odblokować std::unique_lock
. std::lock_guard
zostaną zablokowane tylko raz na budowie i odblokowane po zniszczeniu.
Tak więc dla przypadku użycia B zdecydowanie potrzebujesz std::unique_lock
zmiennej warunkowej. W przypadku A zależy, czy musisz ponownie zablokować strażnika.
std::unique_lock
ma inne cechy, które pozwalają mu np .: być budowanym bez natychmiastowego blokowania muteksu, ale budować opakowanie RAII (patrz tutaj ).
std::lock_guard
zapewnia również wygodne opakowanie RAII, ale nie może bezpiecznie zablokować wielu muteksów. Może być używany, gdy potrzebujesz otoki dla ograniczonego zakresu, np .: funkcja członka:
class MyClass{
std::mutex my_mutex;
void member_foo() {
std::lock_guard<mutex_type> lock(this->my_mutex);
/*
block of code which needs mutual exclusion (e.g. open the same
file in multiple threads).
*/
//mutex is automatically released when lock goes out of scope
};
Aby wyjaśnić tę sprawę w drodze chmike, domyślnie std::lock_guard
i std::unique_lock
są takie same. Tak więc w powyższym przypadku można zastąpić std::lock_guard
z std::unique_lock
. Jednak std::unique_lock
może mieć nieco więcej kosztów ogólnych.
Zauważ, że w dzisiejszych czasach należy używać std::scoped_lock
zamiast std::lock_guard
.
std::lock_guard
wystarczy dla twojego przypadku A, powinieneś go użyć. Nie tylko pozwala uniknąć niepotrzebnego obciążenia, ale także pokazuje czytelnikowi zamiar, że nigdy nie odblokujesz tej osłony.unique_lock
prawdopodobnie zmaleje przez koszt faktycznego zablokowania i odblokowania muteksu (jeśli kompilator nie zoptymalizuje tego narzutu, co może być możliwe).So for usecase B you definitely need a std::unique_lock for the condition variable
- tak, ale tylko w tym wątkucv.wait()
, ponieważ ta metoda atomowo uwalnia muteks. W drugim wątku, w którym aktualizujesz zmienne współdzielone, a następnie wywołujeszcv.notify_one()
,lock_guard
wystarczy zablokować muteks w zasięgu ... chyba że robisz coś bardziej skomplikowanego, czego nie wyobrażam sobie! np. en.cppreference.com/w/cpp/thread/condition_variable - działa dla mnie :)lock_guard
iunique_lock
są prawie tym samym;lock_guard
jest ograniczoną wersją z ograniczonym interfejsem.lock_guard
Zawsze posiada zamek od jego budowy do jej zniszczenia.unique_lock
Mogą być tworzone bez natychmiastowego blokowania, można odblokować w dowolnym momencie swojego istnienia i może przenieść własność zamka z jednej instancji do drugiej.Więc zawsze korzystasz
lock_guard
, chyba że potrzebujesz możliwościunique_lock
.condition_variable
Potrzebujeunique_lock
.źródło
A condition_variable needs a unique_lock.
- tak, ale tylko powait()
stronie ing, jak rozwinięto w moim komentarzu do inf.Używaj,
lock_guard
chyba że musisz ręcznieunlock
muteksować bez niszczenialock
.W szczególności
condition_variable
odblokowuje muteks, gdy idziesz spać po wezwaniach dowait
. Dlatego alock_guard
nie jest tutaj wystarczające.źródło
lock_guard
i odblokować go, tym samym tymczasowo przerywając niezmiennik klasy wartownika. Mimo że zdarza się to niewidoczne dla użytkownika, uważam, że jest to uzasadniony powód, dla którego nie zezwala się na korzystanielock_guard
w tym przypadku.lock_guard
ogóle nie pozwala na odzyskanie bazowego muteksu. Jest to celowe ograniczenie, aby umożliwić prostsze rozumowanie na temat kodu, który używa,lock_guard
w przeciwieństwie do kodu, który używaunique_lock
. Jedynym sposobem na osiągnięcie tego, o co prosisz, jest celowe przerwanie enkapsulacjilock_guard
klasy i wystawienie jej implementacji na inną klasę (w tym przypadku nacondition_variable
). Jest to trudna cena za wątpliwą korzyść użytkownika zmiennej warunkowej, która nie musi pamiętać różnicy między tymi dwoma typami zamków.condition_variable_any.wait
będzie działał zlock_guard
? Norma wymaga dostarczonego typu zamka, aby spełnićBasicLockable
wymaganie (§30.5.2), colock_guard
nie spełnia. Tylko bazowy muteks ma, ale z powodów, o których wspomniałem wcześniej, interfejslock_guard
nie zapewnia dostępu do muteksu.Są pewne rzeczy wspólne między
lock_guard
aunique_lock
i pewnymi różnicami.Ale w kontekście zadanego pytania kompilator nie zezwala na użycie
lock_guard
kombinacji w połączeniu ze zmienną warunkową, ponieważ gdy wywołanie wątku czeka na zmienną warunkową, muteks zostaje odblokowany automatycznie, a inne powiadomienia / wątek powiadamiają o tym bieżący wątek jest wywoływany (wychodzi z oczekiwania), zamek jest ponownie nabywany.Zjawisko to jest sprzeczne z zasadą
lock_guard
.lock_guard
można zbudować tylko raz, a zniszczyć tylko raz.Dlatego
lock_guard
nie można go używać w połączeniu ze zmienną warunkową, aleunique_lock
można to zrobić (ponieważunique_lock
można go kilkakrotnie zablokować i odblokować).źródło
he compiler does not allow using a lock_guard in combination with a condition variable
To nieprawda. To z pewnością nie pozwoli i pracy doskonale zlock_guard
nanotify()
ing stronie. Tylkowait()
strona int wymaga aunique_lock
, ponieważwait()
musi zwolnić blokadę podczas sprawdzania warunku.Nie są tak naprawdę takimi samymi muteksami,
lock_guard<muType>
mają prawie takie same jakstd::mutex
, z tą różnicą, że ich żywotność kończy się na końcu zakresu (nazywany D-tor), więc jasna definicja tych dwóch muteksów:I
Oto przykładowa implementacja:
W tym przykładzie użyłem
unique_lock<muType>
zcondition variable
źródło
Jak wspomnieli inni, std :: unique_lock śledzi status blokady muteksu, dzięki czemu można odroczyć blokowanie do momentu zbudowania blokady i odblokować przed zniszczeniem blokady. std :: lock_guard nie pozwala na to.
Wydaje się, że nie ma powodu, dla którego funkcje oczekiwania std :: condition_variable nie powinny brać blokady lock_guard, jak również unikalnego_locka, ponieważ za każdym razem, gdy kończy się oczekiwanie (z dowolnego powodu) muteks jest automatycznie odzyskiwany, aby nie spowodował żadnego naruszenia semantycznego. Jednak zgodnie ze standardem, aby użyć std :: lock_guard ze zmienną warunku, musisz użyć std :: condition_variable_any zamiast std :: condition_variable.
Edycja : usunięto „Korzystanie z interfejsu pthreads std :: condition_variable i std :: condition_variable_any powinny być identyczne”. Przyglądając się implementacji gcc:
źródło