9

静的変数(単純なint)を含むこの静的クラスがあります。lock()スレッドのメソッドにを実装したRun()ので、他のスレッドがこのクラスに同時にアクセスすることはできませんが、変数はまだ狂っており、重複、めちゃくちゃ高い値などが表示されます。

これはクラスです:

public static class ExplorationManager
{
    public static int Counter = 0;

    public static void ExplorerMaker(List<int[]> validPaths, List<string> myParents, string[,] myExplorationMap, List<int[]> myPositions)
    {
        foreach (var thread in validPaths.Select
        (path => new Explorer(myParents, path, myExplorationMap, myPositions)).
        Select(explorer => new Thread(explorer.Explore)))
            {
                thread.Name = "Thread of " + Counter + " generation";
                Counter++; 
                thread.Start();
    }
}

}

この変数を「もっと」スレッドセーフにする方法はありますか?

4

6 に答える 6

34

このタイプの安全性を高めるために対処する必要がある少なくとも2つの問題があります。

最初のものは作ることCounter privateです。現在の形式では、変数は100%パブリックであり、アプリケーション内の任意のコードによって変更できます。今日は安全かもしれませんが、明日間違いを犯さないように保護するものは何もありません。それでも他のコードでプロパティを読み取れるようにする場合は、アクセサーを使用します

private static int m_counter;
public static int Counter {
  get { return m_counter; }
}

2番目の問題は、++スレッド間で共有されている場所での安全な操作ではないことです。次のコードに展開されます

Counter = Counter + 1;

これは実際にやっています

  1. ロードカウンター
  2. 負荷1
  3. 追加
  4. ストアカウンター

スレッドは事実上いつでも中断できます。ステップ1、2、または3で1つのスレッドが中断され、別のスレッドがシーケンスを完全に実行すると、古い値を追加/保存することになります。++これが安全でない理由です。スレッド間で共有値をインクリメントする安全な方法は、を使用することInterlocked.Incrementです。この目的のために正確に設計されています

Interlocked.Increment(ref m_counter);
于 2012-10-19T19:46:28.620 に答える
17

Interlockedクラスを使用します。

Interlocked.Increment(ref Counter);
于 2012-10-19T19:37:51.767 に答える
7

lock静的変数のすべての読み取り/書き込みを使用する必要があります。何かのようなもの:

public static readonly object CounterLock = new object();

...
lock ( CounterLock )
{
    Counter++;
}
...

重要なのは、すべての読み取り/書き込みをロックで保護する必要があるということです。単一の場所を保護するだけでは不十分です。他の場所でロックが有効になっている場合でも、読み取りまたは書き込みを行うスレッドが変更を加える可能性があるためです。

ロックは変数ではなくコードの領域を保護するため、共有変数にアクセスするすべての場所でロックが必要になります。

変数をロックすることはできないことに注意してくださいCounter。値型ではなく、参照型のインスタンスをロックとして必要とします。これが私objectがロックタイプとして使用した理由です(他の答えも同じでした)。

于 2012-10-19T19:34:22.590 に答える
3

このような何かがトリックを行う必要があります:

public static class ExplorationManager
{
    public static int Counter = 0;
    private static object _lock = new object();

    public static void ExplorerMaker(List<int[]> validPaths, List<string> myParents, string[,] myExplorationMap, List<int[]> myPositions)
    {
        foreach (var thread in validPaths.Select
        (path => new Explorer(myParents, path, myExplorationMap, myPositions)).
        Select(explorer => new Thread(explorer.Explore)))
            {
                thread.Name = "Thread of " + Counter + " generation";
                lock(_lock)
                {
                    Counter++; 
                    thread.Start();
                }
    }
}
于 2012-10-19T19:35:17.723 に答える
2

Interlocked.Incrementは、もう1つのスレッドセーフオプションです。カウンターが必要な場合は、非常に簡単に使用できます。

var newCounter = Interlocked.Increment(ref Counter)
thread.Name = "Thread of " + (newCounter-1) + " generation";
于 2012-10-19T19:41:09.793 に答える
1

静的コンストラクターを使用して、静的変数を初期化することができます。lockingロックの粒度を適切に制御できるように、個別のオブジェクトを提供することをお勧めします。

于 2012-10-19T19:34:57.920 に答える