A mutex (a mutual exclusion, azaz kölcsönös kizárás rövidítése) egy olyan szinkronizációs eszköz a többszálú programozásban, amely lehetővé teszi, hogy egy megosztott erőforrást egyszerre csak egy szál használjon egy adott kritikus szakaszban.
Ha több szál megfelelő szinkronizáció nélkül fér hozzá ugyanahhoz a megosztott adathoz, és legalább az egyik szál módosítja azt, akkor C++-ban adatverseny (data race) alakulhat ki. Ez nem definiált működéshez vezethet.
A mutex úgy működik, mint egy zár: az a szál, amelyik éppen dolgozni akar az adattal, “lezárja” a mutexet, a többi szál pedig várakozni kényszerül, amíg a zár fel nem szabadul.
Analógia
Képzelj el egy egyfős mosdót egy irodában.
- Bemegy valaki a mosdóba és magára zárja az ajtót (mutex lezárása:
lock). - Amíg bent van, senki más nem tud bemenni. Ha más is szeretné használni a mosdót, _várakozik az ajtó előtt.
- Amikor a bent lévő végzett, kinyitja az ajtót és távozik (mutex feloldása:
unlock). - A kint várakozók közül valaki bemehet a mosdóba és újra bezárhatja az ajtót.
Mutex használatánál nem tudjuk, hogy a várakozók közül kinek lesz lehetősége következőként bejutni a mosdóba, azaz nem tudható, hogy melyik szál tudja majd magához ragadni és zárolni a mutexet.
Mutex C++-ban
A C++11-es szabvány óta a C++ támogatja a szálkezelést és a mutexeket a Standard Template Library <mutex> header állományán keresztül. A legfontosabb osztály az std::mutex.
Manuális lezárás és feloldás mutexen keresztül
Az std::mutex típusú objektum lezárható és feloldható a lock() és unlock() metódusokkal. Ez a naív használat viszont veszélyes, mert ha a lock() és unlock() közötti kódban kivétel dobódik el vagy korai kilépés (early return) lesz az adott függvényből, akkor a zárolt mutex feloldása nem történik meg, akkor a zárolt mutex feloldása elmarad, ezért a többi szál akár végleg várakozhat a mutexre.
| |
Javasolt és biztonságos mutex használat
Annak érdekében, hogy a lezárt mutex a kritikus szakaszt követően mindenképp feloldásra kerüljön érdemes az std::lock_guard osztályt használni. Ez egy RAII elvű burkoló osztály, aminek a konstruktora lezárja a paraméterként átadott mutexet, destruktora pedig feloldja azt. Így amikor az std::lock_guard típusú objektum kikerül a hatókörből, akkor a lezárt mutex feloldása biztosan megtörténik. Így az std::lock_guard típusú objektum élettartama össze lesz kapcsolva a mutex zárolásával és feloldásával.
| |
Mit érdemes még megismerni std::mutex-hez kapcsolódóan?
std::unique_locktípusstd::scoped_locktípustry_lock()metódus- több mutex egyidejű kezelése
- deadlock
- mutex és
std::condition_variable