アンソニー・ウィリアムズ「アクションでC++の同時実行性」という本、7.2.1項では、ロックフリースタックの実装が表示されている中:次にこのロックフリースタッククラスのノードを「削除」すると競合状態が発生するのはなぜですか?
template <typename T>
class lock_free_stack {
struct node {
shared_ptr<T> data_;
node* next_;
node(const T& data) : data_(make_shared(data)) {}
};
atomic<node*> head_;
public:
void push(const T& data)
{
node* new_node = new node(data);
new_node->next_ = head_.load();
while(!head.compare_exchange_weak(new_node->next_, new_node));
}
shared_ptr<T> pop()
{
node* old_head = head_.load();
while (old_head &&
!head_.compare_exchange_weak(old_head, head_->next_));
return old_head ? old_head->data_ : shared_ptr<T>();
}
};
7.2.2項では、著者は「言います。 .. pop()では、あるスレッドがノードを削除する競合状態を避けるために、ノードをリークすることを選択しました。別のスレッドはまだ逆参照しようとしているポインタを保持しています。どのようにそれは(ポップを呼び出す複数のスレッドのために来て
shared_ptr<T> pop()
{
node* old_head = head_.load(); // (1)
while (old_head &&
!head_.compare_exchange_weak(old_head, head_->next_)); // (2)
shared_ptr<T> res; // (3)
if (old_head) {
res.swap(old_head->data);
delete old_head;
return res;
} else {
return {};
}
}
2)):
1)、このようなシナリオが起こるかもしれないと、なぜ次のポップ()関数は、競合状態を引き起こす理由を私は理解していません同時に、 'old_head'変数は、行(3)の後に同じノードオブジェクトを指すことができますか?
申し訳ありませんが、それはまだ私にとって明らかではありません。はい、スレッド1は存在しないポインタをたどり、別の変数に既に割り当てられている「次へ」の値を読み込みます。したがって、compare_exchange_weak()の2番目の引数は無効になります。しかし、compare_exchange_weak()が実行されると "head_!= old_head"が検出されるため、これは決して使用されません。 はい、compare_exchange_weak()の引数が無効ですが、使用されることはありません。何が問題ですか?明確にしていただけますか? – Alex
@alexなぜあなたはそれらが等しくないと思いますか?ぶら下がったポインタをたどり、値は* anything *になります。そして、それは 'head _!= old_head_'をどこでチェックしますか?私はそれを見逃しましたか? – Yakk
おそらく私は何かを逃しているでしょうが、私はまだ正確に何を理解することができません。 "head _!= old_head_"がcompare_exchange_weak()内にあることを確認してください。これらの値が等しくない場合、compare_exchange_weak()の2番目の引数は使用されません。 私の理解では、compare_exchange_weak()はメモリからheadの現在の値を原子的に読み込みます。最初のスレッドが起動した後のシナリオでは、レジスタ内のヘッドの値はメモリ内のヘッドの現在の値と等しくありません。したがって、compare_exchange_weak()はそれを見て、2番目の引数(レジスタのheadの無効な値の後に読み込むガベージ)を使用しません。 – Alex