如何使静态变量线程安全

Jea*_*ini 8 c# multithreading

我有这个静态类,它包含一个静态变量(一个简单的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();
    }
}
Run Code Online (Sandbox Code Playgroud)

}

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

Jar*_*Par 29

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

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

private static int m_counter;
public static int Counter {
  get { return m_counter; }
}
Run Code Online (Sandbox Code Playgroud)

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

Counter = Counter + 1;
Run Code Online (Sandbox Code Playgroud)

这实际上在做什么

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

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

Interlocked.Increment(ref m_counter);
Run Code Online (Sandbox Code Playgroud)


Ere*_*mez 17

使用Interlocked类:

Interlocked.Increment(ref Counter);
Run Code Online (Sandbox Code Playgroud)


xxb*_*bcc 6

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

public static readonly object CounterLock = new object();

...
lock ( CounterLock )
{
    Counter++;
}
...
Run Code Online (Sandbox Code Playgroud)

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

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

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