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

为了提高这种类型的安全性,您至少需要解决两个问题。

第一个是制作Counter private。在当前的形式中,该变量是 100% 公开的,它可以被应用程序中的任何代码改变。今天它可能是安全的,但没有什么可以保护你明天不犯错误。如果您仍然希望其他代码能够读取该属性,请使用访问器

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

第二个问题是++在线程之间共享的位置上的操作不是安全的。它扩展为以下代码

Counter = Counter + 1;

这实际上是在做什么

  1. 负载计数器
  2. 加载 1
  3. 添加
  4. 专卖店柜台

几乎可以随时中断线程。如果一个线程在步骤 1、2 或 3 被中断,而另一个线程完全执行该序列,那么您最终将添加/存储过时的值。这就是++不安全的原因。在线程之间增加共享值的安全方法是使用Interlocked.Increment. 它正是为此目的而设计的

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

使用互锁类:

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是另一个线程安全的选项。如果您只需要计数器,使用起来非常简单。

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 回答