4

複数のスレッドからの大量の受信データを処理しているとします。特定の基準が満たされたときに、このデータを特定のアクションのトリガーとして機能させたい場合があります。ただし、アクションは再入可能ではありません。したがって、同じトリガーを2回続けてすばやく起動すると、アクションは1回だけ実行されます。

bool競合状態が発生し、2つ(またはそれ以上)のスレッドが結局同じアクションを同時に実行する可能性があるため、単純なフラグを使用してアクションが現在実行されているかどうかを示すことは、堅牢なソリューションではありません。もちろん、同期オブジェクトのステートメント内boolは機能します。lockしかし、私は通常、可能な限りロックを避けることを好みます*。

現在、これらの場合に私が行っているのはAutoResetEvent、本質的にアトミックスイッチの形式として使用することです。通常、コードは次のようになります。

if (conditionMet && readyForAction.WaitOne(0))
{
    try
    {
        doAction();
    }
    finally
    {
        readyForAction.Set();
    }
}

これは機能しますが、これはクラスの理想的なユースケースではない可能性があることに気づきましたAutoResetEvent。これはこの問題の適切な解決策だと思いますか、それとも他のメカニズムを使用する方が理にかなっていますか?


更新Jonが指摘したMonitor.TryEnterように、ロック戦略として使用することは(単純なものではなく、lockほぼ同等であると私は信じていますMonitor.Enter)、非常に簡単です。

if (conditionMet && Monitor.TryEnter(lockObject))
{
    try
    {
        doAction();
    }
    finally
    {
        Monitor.Exit(lockObject);
    }
}

そうは言っても、私はヘンクの使用するという考えに強く賛成する傾向がありInterlockedます。それはそれが得るのと同じくらい簡単に思えます。

4

2 に答える 2

3

単純な(そして速い)でSystem.Threading.Interlocked.CompareExchange()十分です。

//untested
int flag = 0;

if (conditionMet && Interlocked.CompareExchange(ref flag, 1, 0) == 0)
{
    try
    {
        doAction();
    }
    finally
    {
        flag = 0; // no need for locking
    }
}
于 2010-09-09T06:41:32.807 に答える
2

「私は通常、可能な限りロックを避けることを好みます」-なぜですか?ここでは、非常によく似た処理を効果的に行っているようですが、CLRの代わりにWindows同期プリミティブを使用しています。一度に1つのスレッドだけが特定のコードを実行できるように同期しようとしています...それは確かに私にはロックのように聞こえます。唯一の違いは、ロックがすでに保持されている場合は、コードをわざわざ実行したくないということです。

を使用して同じ効果を達成できるはずですがMonitor.TryEnter、個人的には理解しやすいと思います。(それから、私はJavaモニターで「成長」したので、私のバックグラウンドに近づいています。)

于 2010-09-09T06:46:55.163 に答える