はじめに
マルチスレッドプログラミングにおいて、あるスレッドがループの中で、特定の条件が満たされるのをひたすら待ち続ける(ビジーウェイトまたはスピンロック)ような実装を行うことがあります。しかし、このような実装は、CPUリソースを無駄に消費し、他のスレッドの実行を妨げてしまう可能性があります。
このような状況で、std::this_thread::yield() 関数を呼び出すと、現在のスレッドは「今はCPUを使いません。どうぞ他のスレッドにCPUを割り当ててください」と、OSのスケジューラに自発的に通知します。
この記事では、yield() がどのような場面で役立つのかを、サンプルコードと共に解説します。
yield() を使ったサンプルコード
このコードは、グローバル変数 isReady が true になるのを待つワーカースレッドを実装します。yield() を使うことで、メインスレッドが isReady を変更するためのCPU時間を確保しやすくします。
完成コード
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic> // atomic を使うために必要
using namespace std;
// スレッド間で共有されるフラグ (atomicでスレッドセーフにする)
atomic<bool> isReady(false);
// isReadyフラグがtrueになるのを待つワーカースレッド
void worker_thread() {
cout << "ワーカースレッド: 準備ができるまで待機します..." << endl;
// isReadyがfalseの間、ループを続ける (ビジーウェイト)
while (!isReady) {
// CPUの制御を他のスレッドに明け渡す
this_thread::yield();
}
cout << "ワーカースレッド: 準備ができたので、処理を実行します。" << endl;
}
int main() {
thread worker(worker_thread);
cout << "メインスレッド: 2秒後に準備を完了させます..." << endl;
this_thread::sleep_for(chrono::seconds(2));
isReady = true; // 共有フラグをtrueにする
worker.join(); // ワーカースレッドの終了を待つ
cout << "メインスレッド: 処理が完了しました。" << endl;
return 0;
}
コードの解説
while (!isReady) { this_thread::yield(); }
これが yield() の典型的な使われ方です。
while (!isReady):worker_threadは、isReadyフラグがtrueになるまで、このループをひたすら回り続けます。this_thread::yield(): ループの中でyield()を呼び出すことで、worker_threadはCPUのタイムスライスを使い切る前に、自発的にCPUを解放します。これにより、OSのスケジューラは、mainスレッドのような、他に実行を待っているスレッドにCPUを割り当てる機会を得やすくなります。
もし yield() がないと、worker_thread がCPUを独占し続けてしまい、mainスレッドが isReady = true; を実行する機会がなかなか回ってこず、結果としてプログラム全体のパフォーマンスが低下する可能性があります。
yield() を使うべきか?
yield() は、非常に低レベルな同期プリミティブであり、その振る舞いはOSのスケジューリング戦略に大きく依存するため、通常は yield() を直接使うことは推奨されません。
yield() を使ったビジーウェイトは、ミューテックス (std::mutex)、条件変数 (std::condition_variable)、セマフォ (std::semaphore) といった、より高レベルで効率的な同期メカニズムで代替できる場合がほとんどです。これらの高レベルなツールは、スレッドを無駄にCPUを消費させる「実行中」状態ではなく、効率的な「待機」状態に移行させることができます。
yield() は、他の同期メカニ-“ズムが使えない、非常に特殊でパフォーマンスがクリティカルな状況でのみ、最後の手段として検討すべきです。
まとめ
今回は、現在のスレッドの実行を一時的に中断し、他のスレッドにCPUを譲る std::this_thread::yield() について解説しました。
yield()は、現在のスレッドのタイムスライスを放棄し、OSに再スケジューリングを促すヒント。- 主にビジーウェイトループの中で、CPUの無駄な消費を緩和するために使われる。
- 一般的には、ミューテックスや条件変数といった、より高レベルな同期手法を使う方が望ましい。
