現代のC++(C++11以降)では、マルチスレッド処理のための豊富なツールが存在します:std::thread、ミューテックスおよびリソースへのアクセスを管理するためのさまざまなラッパーオブジェクト。スレッド間の安全な相互作用には、以下のコンポーネントの理解が必要です:
レース状態を避けるためには、可能な限りRAIIラッパーを使用してください。同じミューテックスを再度取得しようとするとデッドロックにつながるため注意が必要です(再帰的ミューテックスでない限り)。
正しい使用例:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void printThreadSafe(int id) { std::lock_guard<std::mutex> lock(mtx); std::cout << "スレッド " << id << std::endl; } int main() { std::thread t1(printThreadSafe, 1); std::thread t2(printThreadSafe, 2); t1.join(); t2.join(); }
std::lock_guardstd::mutexを使用してstd::condition_variableによる条件同期を実装できますか?
回答: いいえ!std::condition_variableを使うためには必ずstd::unique_lockstd::mutexが必要です。なぜなら、lock_guardはロックの制御(たとえば、一時的に解放する— unlock — し、再度取得する必要がある)を行うメソッドを提供しないからです。
例:
std::mutex mtx; std::condition_variable cv; bool ready = false; void waitForEvent() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{return ready;}); // その後の処理 }
逸話
プロジェクトでは手動でmtx.lock()/mtx.unlock()を使用していました。lock/unlockの間に例外が発生するとミューテックスがロックされたままになり、多くのスレッドが「フリーズ」しました。RAII(
std::lock_guard)に置き換えたところ、問題が解決しました。
逸話
std::condition_variableで通知を行う際にstd::unique_lockの代わりにstd::lock_guardを使用しました。その結果、新しいコンパイラではコードがコンパイルされず、古いコンパイラではAPIの不整合のためにランタイムアサートが発生しました。
逸話
std::lock_guardのunlock()を呼び出して「手動」のミューテックスアクセス制御を行っていました。これにより、異なるコンパイラで未定義の動作が発生し、アプリケーションがクラッシュすることもありました。lock_guardはマニュアルリリースをサポートしておらず、unique_lockへの移行が必要であることが理解されました。