オブジェクトを変更する可能性のある 1 回のアクセスで複数のスレッドからオブジェクトにアクセスする場合は、いくつかの同期が必要です。説明したシナリオでは、必要なスレッド保護とシグナル伝達を行うキュー クラスが必要になる場合があります。簡単な実装を次に示します。
#include <mutex>
#include <condition_variable>
#include <deque>
template <typename T>
class queue
{
private:
std::mutex d_mutex;
std::condition_variable d_condition;
std::deque<T> d_queue;
public:
void push(T const& value) {
{
std::unique_lock<std::mutex> lock(this->d_mutex);
d_queue.push_front(value);
}
this->d_condition.notify_one();
}
T pop() {
std::unique_lock<std::mutex> lock(this->d_mutex);
this->d_condition.wait(lock, [=]{ return !this->d_queue.empty(); });
T rc(std::move(this->d_queue.back()));
this->d_queue.pop_back();
return rc;
}
};
このコードは C++ 2011 の構造を使用していますが、標準化されていないことを除いて、それらの使用を避け、代わりに C++ 2003 の構造を使用するように簡単に変更できます。
重要なポイントは次のとおりです。
- A
std::mutex
は、スレッドのみが一度にキューにアクセスすることを確認するために使用されます。
- 何かをキューに入れると、ミューテックスのロックが取得され、オブジェクトがキューに挿入されます。オブジェクトが挿入されると、ロックが自動的に解除され、条件変数が通知されます。
- キューから何かを抽出すると、ミューテックスのロックが取得され、スレッドは条件変数を使用してキューが空でなくなるのを待ちます: キューに何かが既にある場合、条件はすぐに返されます
true
。wait()
条件が再評価される時点でシグナルを受け取るまでスリープ状態になります。条件変数への偽のウェイクアップがある可能性があるため、条件が複数回評価される場合があることに注意してください。
- ラムダはそのコンテキストを値でキャプチャします。実際にキャプチャするのは
this
;だけです。メンバー変数はローカル コンテキストの一部ではないため、直接キャプチャすることはできません。
- からの結果
pop()
は値によって返されます。ロックが解放された瞬間にコンテナが変更される可能性があり、オブジェクトが適切な場所に配置されていたとしても、参照によってオブジェクトを返すことができなくなります。
これがおもちゃの例である主な理由は、システムをシャットダウンする適切な方法がないためです。スレッドがキューでブロックされている場合、スレッドは別のオブジェクトが存在するまで待機します。実際の実装では、シャットダウンする時間になったことを通知する何らかの方法があり、おそらく から例外がスローされpop()
ます。また、オブジェクトの抽出を強制的にブロックしないキューがあると便利な場合もあります。代わりに、try_pop()
ロックを取得し、キューが空でないかどうかをチェックし、結果の抽出とオブジェクトまたは信号の失敗に応じてチェックする関数があります。ただし、このような機能は簡単に実装できます。