我有这个静态类,它包含一个静态变量(一个简单的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或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)
您需要使用lock静态变量的所有读/写.就像是:
public static readonly object CounterLock = new object();
...
lock ( CounterLock )
{
Counter++;
}
...
Run Code Online (Sandbox Code Playgroud)
关键是所有读/写必须受到锁的保护 - 它不足以保护单个位置,因为当其他地方的锁生效时,执行读或写的线程仍然可能会发生变化.
锁保护代码区域而不是变量,这就是为什么在访问共享变量时需要锁定的原因.
请注意,您无法锁定Counter变量 - 您需要引用类型的实例作为锁,而不是值类型.这就是我用作object锁定类型的原因(另一个答案也是如此).
| 归档时间: |
|
| 查看次数: |
28464 次 |
| 最近记录: |