public class CrossRoads(){
public int _timeouts;
}
public void TimeIsUp(CrossRoads crossRoads){
crossRoads._timeouts++;
}
public void HowManyTimeOuts(CrossRoads crossRoads){
int timeOuts = crossRoads._timeouts;
}
答案 0 :(得分:12)
简单的答案是,如果从多个线程同时访问上述代码,则能够导致问题。
.Net框架提供了两种解决方案:互锁和线程同步。
对于简单的数据类型操作(即整数),使用Interlocked class的联锁将正常工作,是推荐的方法。
事实上,interlocked提供了特定的方法(递增和递减),使这个过程变得简单:
将IncrementCount方法添加到CrossRoads类:
public void IncrementCount() {
Interlocked.Increment(ref _timeouts);
}
然后从后台工作人员那里打电话:
public void TimeIsUp(CrossRoads crossRoads){
crossRoads.IncrementCount();
}
除非32位操作系统上的64位值,否则读取值是原子的。有关详细信息,请参阅Interlocked.Read method documentation。
对于类对象或更复杂的操作,您需要使用线程synchronization locking(在VB中锁定C#或SyncLock)。
这是通过在要应用锁定的级别(例如,在类中)创建静态同步对象,获取对该对象的锁定,以及在该锁定内执行(仅)必要的操作来实现的: / p>
private static object SynchronizationObject = new Object();
public void PerformSomeCriticalWork()
{
lock (SynchronizationObject)
{
// do some critical work
}
}
答案 1 :(得分:4)
好消息是对int的读写保证是原子的,所以没有撕裂的值。但是,不能保证安全++,并且读取可能会缓存在寄存器中。还有指令重新排序的问题。
我会用:
Interlocked.Increment(ref crossroads._timeouts);
对于写入,这将确保没有值丢失,并且;
int timeouts = Interlocked.CompareExchange(ref crossroads._timeouts, 0, 0);
对于读取,因为这遵循与增量相同的规则。严格来说,“易变”可能足以让人阅读,但人们对它的了解却很少,以至于Interlocked似乎(IMO)更安全。无论哪种方式,我们都在避免锁定。
答案 2 :(得分:3)
好吧,我不是C#开发人员,但这就是它通常在这个级别上工作的方式:
.NET如何处理这种情况?
解锁。不太可能保证是原子的。
我是否必须锁定/门控访问可能有时被写入+从不同线程访问的每个字段/属性?
是。另一种方法是对客户端可用的对象进行锁定,然后告诉客户端在使用实例时必须锁定对象。这将减少锁定采集的数量,并为您的客户保证更加一致,可预测的状态。
答案 3 :(得分:3)
忘记dotnet。在机器语言级别,crossRoads._timeouts++
将实现为INC [memory]
指令。这称为读 - 修改 - 写指令。这些指令对于单个处理器上的多线程*是原子的(基本上是通过时间切片实现的),但对于使用多个处理器或多个内核的多线程而言,它们不是原子的。
所以:
如果您可以保证只有TimeIsUp()
会修改crossRoads._timeouts
,并且如果您可以保证只有一个线程会执行TimeIsUp()
,那么这样做是安全的。 TimeIsUp()
中的写作可以正常工作,HowManyTimeOuts()
(以及其他任何地方)的阅读都可以正常工作。但是如果你也在其他地方修改crossRoads._timeouts
,或者如果你再生成一个后台线程编写器,你将遇到麻烦。
在任何一种情况下,我的建议都是安全地将其锁定。
(*)它们对于单个处理器上的多线程是原子的,因为线程之间的上下文切换发生在周期性中断上,而在x86架构上,这些指令对于中断是原子的,这意味着如果发生中断当CPU正在执行这样的指令时,中断将一直等到指令完成。对于更复杂的指令,例如具有REP前缀的指令,这不适用。
答案 4 :(得分:2)
虽然int
可能是CPU的“原生”大小(一次处理32位或64位),但如果从不同的线程读取和写入同一个变量,最好是锁定这个变量和同步访问。
从来没有保证对int
进行读/写可能是原子的。
您也可以在此处使用Interlocked.Increment
。