如何使静态变量线程安全

时间:2012-10-19 19:31:12

标签: c# multithreading

我有这个静态类,它包含一个静态变量(一个简单的int)。我已经在线程的lock()方法中实现了Run(),因此没有其他线程可以同时访问此类,但变量仍然会变得疯狂,显示重复项,非常高的值等等。 / p>

这是班级:

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();
    }
}

}

有没有办法让这个变量“更”线程安全?

6 个答案:

答案 0 :(得分:28)

为了提高此类安全性,您需要解决至少2个问题。

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

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

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

Counter = Counter + 1;

实际上在做什么

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

    Interlocked.Increment(ref m_counter);
    

答案 1 :(得分:17)

使用Interlocked类:

Interlocked.Increment(ref Counter);

答案 2 :(得分:6)

您需要在静态变量的所有读/写周围使用lock。类似的东西:

public static readonly object CounterLock = new object();

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

关键是所有读/写必须受到锁保护 - 它不足以保护单个地方,因为执行读取或写入的线程仍然可能当其他地方的锁定生效时更改。

锁可以保护代码区域而不是变量,这就是为什么在访问共享变量时需要锁定的原因。

请注意,您无法锁定Counter变量 - 您需要引用类型的实例作为锁,而不是值类型。这就是为什么我使用object作为锁定类型(另一个答案也是如此)。

答案 3 :(得分: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();
                }
    }
}

答案 4 :(得分:1)

您可以尝试使用静态构造函数初始化静态变量。最佳做法是提供单独的locking对象,以便您可以很好地控制锁的粒度。

答案 5 :(得分:1)

Interlocked.Increment是另一个线程安全选项。如果你只是需要反击,那么使用起来很简单。

var newCounter = Interlocked.Increment(ref Counter)
thread.Name = "Thread of " + (newCounter-1) + " generation";