繁体   English   中英

如何使静态变量线程安全

[英]How to make a static variable thread-safe

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

这是班级:

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

}

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

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

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

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

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

Counter = Counter + 1;

这实际上在做什么

  1. 加载计数器
  2. 载入1
  3. 商店柜台

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

Interlocked.Increment(ref m_counter);

使用Interlocked类:

Interlocked.Increment(ref Counter);

您需要在静态变量的所有读/写周围使用lock 就像是:

public static readonly object CounterLock = new object();

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

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

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

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

像这样的东西应该做的伎俩:

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM