今、以下のようなペンの質問があります。
二つのスレッド、1つのスレッドのループ出力がありA、別のスレッドのループ出力Bコンソールでは、これら二つのスレッドに安定した出力を作成する方法は、ABABAB…。
あまり考えずに、私たちは確かに、変数の兆候を定義するisTurnA
、isTurnA
としてtrue
出力A、出力同じトークンBだけステートマシンに従って、それが最も簡単な有限状態機械の一つであり、そして確かな答えを同意するものとします。isTurnA
保護するデータなので、アトミック変数またはミューテックスを共有している、ミューテックスがここで使用されstd::mutex
、コードは次のとおりです。
class TypeAB
{
public:
TypeAB() : _isTurnA(true) {}
~TypeAB()
{
_threadA.join();
_threadB.join();
}
void PrintAB()
{
_threadA = std::thread(&TypeAB::TypeA, this);
std::this_thread::sleep_for(std::chrono::seconds(2));
_threadB = std::thread(&TypeAB::TypeB, this);
}
private:
std::mutex _mutex;
bool _isTurnA;
std::thread _threadA;
std::thread _threadB;
void TypeB()
{
for (int cnt = 0; cnt < 10; std::this_thread::sleep_for(std::chrono::seconds(3)))
{
std::unique_lock<std::mutex> lock(_mutex);
for (; _isTurnA; )
{
lock.unlock();
lock.lock();
}
std::cout << std::this_thread::get_id() << " - B : " << cnt++ << std::endl;
_isTurnA = true;
}
}
void TypeA()
{
for (int cnt = 0; cnt < 10; std::this_thread::sleep_for(std::chrono::seconds(1)))
{
std::unique_lock<std::mutex> lock(_mutex);
for (; !_isTurnA; )
{
lock.unlock();
lock.lock();
}
std::cout << std::this_thread::get_id() << " - A : " << cnt++ << std::endl;
_isTurnA = false;
}
}
};
このコードは実行されているが、次のサイクルを見ています:
for (; !_isTurnA; )
{
lock.unlock();
lock.lock();
}
別のスレッドがこれを行うがかかり効率のように、ロックを取得する機会を持っているのでという、ロックを解除するために続けています。その後、我々は、ブロッキング操作に来ます:
for (; !_isTurnA; )
{
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
lock.lock();
}
これが問題をもたらすだろうと同じように、それだけで完了し、ロックへのアクセスをブロックすることがあり、この時間は、ちょうど別のスレッドを設定しisTurnA
、このような状況は、現在のスレッドが複数回ブロックさせます。したがって、ブロックの時間のバランスを取る、この困難な時期を遮断することが短すぎる、その後、スピンで、違いはありません。時間を遮断することが長すぎる、それはないタイムリーな問題をスレッドにつながります。
これは戻っていたのイベントイベント(条件変数パッケージ上)C ++:次のような方法、それが動作することを、条件変数電車の中でその例
for (; !_isTurnA; )
{
lock.unlock();
// 线程阻塞于此,等待一个信号,有这个信号就不阻塞了
lock.lock();
}
特定には見ることができます(条件変数パッケージ上)C ++イベントイベントで条件変数を導入しました。ここではその質問への答えは、標準的なマルチスレッドの道である、次のとおりです。
#include <bits/stdc++.h>
class TypeAB
{
public:
~TypeAB()
{
_threadA.join();
_threadB.join();
}
void PrintAB()
{
_threadA = std::thread(&TypeAB::TypeA, this);
std::this_thread::sleep_for(std::chrono::seconds(2));
_threadB = std::thread(&TypeAB::TypeB, this);
}
private:
std::mutex _mutex;
std::condition_variable _condi;
bool _isTurnA = true;
std::thread _threadA;
std::thread _threadB;
void TypeB()
{
for (int cnt = 0; cnt < 10; std::this_thread::sleep_for(std::chrono::seconds(3)))
{
std::unique_lock<std::mutex> lock(_mutex);
_condi.wait(lock, [this] () -> bool { return !_isTurnA; });
std::cout << std::this_thread::get_id() << " - B : " << cnt++ << std::endl;
_isTurnA = true;
lock.unlock();
_condi.notify_one();
}
}
void TypeA()
{
for (int cnt = 0; cnt < 10; std::this_thread::sleep_for(std::chrono::seconds(1)))
{
std::unique_lock<std::mutex> lock(_mutex);
_condi.wait(lock, [this] () -> bool { return _isTurnA; });
std::cout << std::this_thread::get_id() << " - A : " << cnt++ << std::endl;
_isTurnA = false;
lock.unlock();
_condi.notify_one();
}
}
};
int main()
{
TypeAB().PrintAB();
return 0;
}
今見て取りstd::condition_variable
、ソースコードのセクションを:
// VC14的mutex头文件
class condition_variable
{ // class for waiting for conditions
public:
typedef _Cnd_t native_handle_type;
condition_variable()
{ // construct
_Cnd_init_in_situ(_Mycnd());
}
~condition_variable() _NOEXCEPT
{ // destroy
_Cnd_destroy_in_situ(_Mycnd());
}
condition_variable(const condition_variable &) = delete;
condition_variable &operator=(const condition_variable &) = delete;
void notify_one() _NOEXCEPT
{ // wake up one waiter
_Cnd_signalX(_Mycnd());
}
void notify_all() _NOEXCEPT
{ // wake up all waiters
_Cnd_broadcastX(_Mycnd());
}
void wait(unique_lock<mutex> &_Lck)
{ // wait for signal
_Cnd_waitX(_Mycnd(), _Lck.mutex()->_Mymtx());
}
template <class _Predicate>
void wait(unique_lock<mutex> &_Lck, _Predicate _Pred)
{ // wait for signal and test predicate
while (!_Pred())
wait(_Lck);
}
}
以下のコード検証の質問:
wait
二勧告の文言は、エラー覚醒を防ぐことができます- 条件変数は、使用するロックを一致させる必要がある、とだけすることができ
std::unique_lock
、二つの理由からstd::unique_lock
ロック解除メンバ関数が正確に適合条件変数を働きがある; RAIIモードは、あなたが例外安全性を保証することができます - 条件変数は、構成およびアサイン設定を(2つのコンストラクタが除去される)をコピーすることができないだけでなく、ああ(右辺値コンストラクタが削除されていない)、移動させることができる
std::condition_variable
構成std::future
予示を