キャンセルのためにフラグを立てるためにブールの上にウェイトハンドルを使用する理由はありますか?
-
28-10-2019 - |
質問
ちょっとしたコードを継承しましたが、それを確認すると、このような構造を見つけています(背景スレッドメソッド内):
private ManualResetEvent stopEvent = new ManualResetEvent(false);
private void Run_Thread() {
while (!stopEvent.WaitOne(0, true)) {
// code here
}
}
通常、パブリックまたはプライベートがあります Stop()
方法、そう:
public void Stop() {
stopEvent.Set();
bgThread.Join();
}
私の質問はこれです:ここで待機ハンドルを使用して何が提供されますか?これは、停止のシグナル伝達が原子操作であることを保証するために行われているように思われますが、とにかくブール奏者への書くことは原子的であると思いました。その場合、次のことを使用しない理由はありますか:
private void Run_Thread() {
while(!stop) {
// code here
}
}
public void Stop() {
stop = true;
bgThread.Join();
}
解決
に書き込みます bool
変数は確かにアトミックですが、変数もそうでない限り 揮発性 (または、他の同期を導入します)その後、他のスレッドから表示されない場合があります。このような問題は、追跡して再現するのが難しい場合があります。
たとえば、X64ラップトップでは、次のプログラムが正しく停止します。私のx86ネットブックでは、永遠に吊るされています。 (両方ともコンパイルされています csc /o+ Test.cs
).
using System;
using System.Threading;
class Test
{
static bool stop = false;
static void Main(string[] args)
{
new Thread(CountLots).Start();
Thread.Sleep(100);
stop = true;
Console.WriteLine("Finished...");
}
static void CountLots()
{
long total = 0;
while (!stop)
{
total++;
}
}
}
この特定のケースでは、揮発性フラグを使用することは合理的と思われますが、.NET 4を使用している場合は、タスクキャンセルメカニズムを使用する方が良いでしょう:)
もちろん、通常、フラグ以外の何かを使用するより良い理由は、何らかの状態を待ちたい場合です - それが「新しいアイテムがあります」または「私はキャンセルされた」(または両方)であろうと、緊密なループをせずに。
他のヒント
ブールで制御 /ブロッキング「待機」を行うことはできません(おそらく必要ではありませんが、とにかくモニターはより軽量になる可能性があります)。
キャンセルとして 国旗 ブールは問題ありませんが、登録キャッシュが問題になるのを防ぐために、ブールを揮発性にしたいと思うかもしれません。
ブールを使用するだけでは機能しません、あなたはそれを宣言する必要があります 揮発性 コードジェネレーターに、値をCPUレジスタに保存しないでください。スレッドがTrueに設定された値が表示される正確な時間は予測不可能であり、コードが実行されるCPUの種類に大きく依存します。
醜い詳細、ウェイトハンドルを使用するときに無視できるもの。
最後の議論は虚偽でなければなりません。