我有这个静态类,它包含一个静态变量(一个简单的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();
}
}
}
有没有办法让这个变量“更”线程安全?
答案 0 :(得分:28)
为了提高此类安全性,您需要解决至少2个问题。
第一个是制作Counter
private
。在它的当前形式中,变量是100%公开的,并且它可以被应用程序中的任何代码片段变异。今天它可能是安全的,但没有什么能保护你明天犯错。如果您仍希望其他代码段能够读取属性,请使用访问器
private static int m_counter;
public static int Counter {
get { return m_counter; }
}
第二个问题是++
对于在线程之间共享的位置不是安全的操作。它扩展为以下代码
Counter = Counter + 1;
实际上在做什么
线程几乎可以随时中断。如果一个线程在步骤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";