std::mutex
Főnév
std::mutex (tsz. std::mutexes)
- (informatika) A std::mutex (mutual exclusion, azaz kölcsönös kizárás) a C++ több szálas programozásának egyik alapvető eszköze, amely lehetővé teszi az erőforrások biztonságos megosztását és a versenyhelyzetek elkerülését. Ebben a részletes útmutatóban bemutatjuk a std::mutex működését, a különböző szinkronizációs technikákat és a legjobb gyakorlatokat.
1. Miért van szükség std::mutex-re?
Ha egy program több szálon (thread) fut, és ezek a szálak közösen használnak egy változót vagy más erőforrást, akkor versenyhelyzet (race condition) alakulhat ki. Ez azt jelenti, hogy az egyik szál éppen módosítja az értéket, miközben egy másik szál is próbálja azt elérni. Ennek következménye lehet: - Hibás adatok (nem az elvárt eredmény) - Memóriakorrupció - Program összeomlása
Példa versenyhelyzetre:
#include <iostream>
#include <thread>
int szamlalo = 0;
void novel() {
for (int i = 0; i < 1000; i++) {
szamlalo++; // Versenyhelyzet
}
}
int main() {
std::thread szal1(novel);
std::thread szal2(novel);
szal1.join();
szal2.join();
std::cout << "Számláló értéke: " << szamlalo << std::endl; // Nem biztos, hogy 2000 lesz!
return 0;
}
Mivel a két szál egyszerre próbálja módosítani a szamlalo változót, a végeredmény nem garantáltan lesz 2000.
2. std::mutex használata
A std::mutex segítségével megakadályozhatjuk, hogy két szál egyszerre férjen hozzá egy erőforráshoz.
std::mutex példa
#include <iostream>
#include <thread>
#include <mutex>
int szamlalo = 0;
std::mutex mtx; // Mutex létrehozása
void novel() {
for (int i = 0; i < 1000; i++) {
mtx.lock(); // Mutex zárolása
szamlalo++;
mtx.unlock(); // Mutex feloldása
}
}
int main() {
std::thread szal1(novel);
std::thread szal2(novel);
szal1.join();
szal2.join();
std::cout << "Számláló értéke: " << szamlalo << std::endl; // Garantáltan 2000 lesz
return 0;
}
Itt az mtx.lock() biztosítja, hogy egyszerre csak egy szál módosíthassa a szamlalo változót, és az mtx.unlock() felszabadítja a mutexet.
3. std::lock_guard – Automatikus mutex kezelés
A std::mutex kézi kezelése során elfelejthetjük meghívni az unlock() függvényt, például kivételek esetén. Ezt a problémát a std::lock_guard oldja meg, amely automatikusan zárolja és feloldja a mutexet.
#include <iostream>
#include <thread>
#include <mutex>
int szamlalo = 0;
std::mutex mtx;
void novel() {
for (int i = 0; i < 1000; i++) {
std::lock_guard<std::mutex> lock(mtx); // Automatikus zárolás és feloldás
szamlalo++;
}
}
int main() {
std::thread szal1(novel);
std::thread szal2(novel);
szal1.join();
szal2.join();
std::cout << "Számláló értéke: " << szamlalo << std::endl;
return 0;
}
A std::lock_guard biztosítja, hogy a mutex mindig feloldásra kerüljön, amikor a lock változó élettartama véget ér.
4. std::unique_lock – Rugalmas mutex kezelés
A std::unique_lock a std::lock_guard egy rugalmasabb verziója, amely lehetővé teszi a mutex későbbi zárolását vagy korábbi feloldását.
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void dolgozo() {
std::unique_lock<std::mutex> lock(mtx);
std::cout << "Dolgozó szál fut" << std::endl;
lock.unlock(); // Korábban feloldhatjuk a mutexet
}
int main() {
std::thread szal(dolgozo);
szal.join();
return 0;
}
A std::unique_lock használata: - lock.unlock() lehetővé teszi, hogy a mutexet előbb feloldjuk, ha már nincs rá szükség. - Támogatja a defer_lock, try_to_lock és adopt_lock módokat is.
5. Deadlock – Holtverseny és elkerülése
A deadlock akkor fordul elő, ha két vagy több szál végtelenül vár egymásra, hogy felszabadítsanak egy erőforrást.
Deadlock példa
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx1, mtx2;
void f1() {
mtx1.lock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
mtx2.lock(); // Deadlock lehetséges
std::cout << "f1 fut" << std::endl;
mtx2.unlock();
mtx1.unlock();
}
void f2() {
mtx2.lock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
mtx1.lock(); // Deadlock lehetséges
std::cout << "f2 fut" << std::endl;
mtx1.unlock();
mtx2.unlock();
}
int main() {
std::thread t1(f1);
std::thread t2(f2);
t1.join();
t2.join();
return 0;
}
Itt deadlock léphet fel, ha az egyik szál az mtx1-et, a másik pedig az mtx2-t foglalja le először, majd mindkettő vár a másikra.
Deadlock elkerülése – std::lock()
A std::lock() lehetővé teszi, hogy egyszerre több mutexet is zároljunk, így elkerülhető a holtverseny.
void f1() {
std::lock(mtx1, mtx2);
std::cout << "f1 fut" << std::endl;
mtx1.unlock();
mtx2.unlock();
}
6. std::try_lock – Nem blokkoló mutex zárás
A std::try_lock() függvény megpróbálja zárolni a mutexet, de nem vár, ha az már zárolva van.
void dolgozo() {
if (mtx.try_lock()) {
std::cout << "Szál dolgozik" << std::endl;
mtx.unlock();
} else {
std::cout << "Másik szál használja a mutexet" << std::endl;
}
}
Összegzés
A std::mutex egy alapvető eszköz a több szálas programozásban, amely biztosítja az adatok konzisztenciáját és megakadályozza a versenyhelyzeteket. Az olyan eszközök, mint a std::lock_guard, std::unique_lock és std::lock(), segítenek az erőforrások hatékony és biztonságos kezelésében.
Főbb tanulságok
✅ Használj mutexet, ha egy erőforráshoz több szál is hozzáfér.
✅ Használj std::lock_guard-ot, ha egy szál rövid ideig tartja zárolva a mutexet.
✅ Használj std::unique_lock-ot, ha rugalmas zárolásra van szükséged.
✅ Használj std::lock()-ot, ha több mutexet kell egyszerre zárolni és el akarod kerülni a deadlockot.
- std::mutex - Szótár.net (en-hu)
- std::mutex - Sztaki (en-hu)
- std::mutex - Merriam–Webster
- std::mutex - Cambridge
- std::mutex - WordNet
- std::mutex - Яндекс (en-ru)
- std::mutex - Google (en-hu)
- std::mutex - Wikidata
- std::mutex - Wikipédia (angol)