W nowoczesnym C++ (od C++11) istnieje bogaty zestaw narzędzi do pracy z wielowątkowością: std::thread, mutexy i różne obiekty opakowujące do zarządzania dostępem do zasobów. Bezpieczna interakcja między wątkami wymaga zrozumienia następujących komponentów:
Aby uniknąć wyścigów, zawsze używaj opakowań RAII tam, gdzie to możliwe. Pamiętaj, że próba ponownego przejęcia tego samego mutexu doprowadzi do deadlocku (o ile nie jest to rekurencyjny mutex).
Przykład poprawnej pracy:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void printThreadSafe(int id) { std::lock_guard<std::mutex> lock(mtx); std::cout << "Wątek " << id << std::endl; } int main() { std::thread t1(printThreadSafe, 1); std::thread t2(printThreadSafe, 2); t1.join(); t2.join(); }
Czy można używać std::lock_guardstd::mutex do realizacji warunkowej synchronizacji z std::condition_variable?
Odpowiedź: Nie! Do pracy z std::condition_variable koniecznie potrzebny jest std::unique_lockstd::mutex, ponieważ lock_guard nie udostępnia metod do kontrolowania blokady (na przykład, tymczasowego zwalniania — unlock — a następnie ponownego przejmowania), które są niezbędne podczas oczekiwania na powiadomienie.
Przykład:
std::mutex mtx; std::condition_variable cv; bool ready = false; void waitForEvent() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{return ready;}); // Dalsze przetwarzanie }
Historia
W projekcie stosowano ręczną obsługę mtx.lock()/mtx.unlock(). Przy wystąpieniu wyjątku między lock/unlock, mutex pozostawał zablokowany — wiele wątków "zawieszało" się. Po zamianie na RAII (
std::lock_guard) problem zniknął.
Historia
Przy powiadamianiu przez std::condition_variable zamiast std::unique_lock używano std::lock_guard. W rezultacie, program nie kompilował się na nowych kompilatorach, a na starym — występował assert czasu wykonywania z powodu niespójnego API.
Historia
W kodzie wywoływano unlock() na std::lock_guard dla "ręcznej" kontroli dostępu do mutexu. To prowadziło do nieokreślonego zachowania na różnych kompilatorach, czasami do awarii aplikacji. Zrozumiano, że lock_guard nie wspiera ręcznego zwalniania, potrzebna była migracja do unique_lock.