(这是重复:如何正确读取Interlocked.Increment'ed int字段?但是,在阅读了答案和评论之后,我仍然不确定正确的答案.)
有些代码我不拥有,也无法更改为使用在几个不同线程中增加int计数器(numberOfUpdates)的锁.所有通话都使用:
Interlocked.Increment(ref numberOfUpdates);
Run Code Online (Sandbox Code Playgroud)
我想在我的代码中读取numberOfUpdates.既然这是一个int,我知道它不会撕裂.但是,确保我获得最新价值的最佳方法是什么?看起来我的选择是:
int localNumberOfUpdates = Interlocked.CompareExchange(ref numberOfUpdates, 0, 0);
Run Code Online (Sandbox Code Playgroud)
要么
int localNumberOfUpdates = Thread.VolatileRead(numberOfUpdates);
Run Code Online (Sandbox Code Playgroud)
两者都有效(无论优化,重新排序,缓存等,都可以提供最新的价值)?一个比另一个更受欢迎吗?还有第三种选择更好吗?
我想了解如何编写线程安全代码.
例如,我在我的游戏中有这个代码:
bool _done = false;
Thread _thread;
// main game update loop
Update()
{
// if computation done handle it then start again
if(_done)
{
// .. handle it ...
_done = false;
_thread = new Thread(Work);
_thread.Start();
}
}
void Work()
{
// ... massive computation
_done = true;
}
Run Code Online (Sandbox Code Playgroud)
如果我理解正确,可能会发生主游戏线程和我_thread可以拥有自己的缓存版本_done,并且一个线程可能永远不会_done在另一个线程中看到更改?
如果可能,如何解决?
是否可以解决,只应用volatile关键字.
或者是有可能读取和写入,通过价值Interlocked的类似的方法Exchange和Read?
如果我包围_done读写操作lock (_someObject),需要我使用Interlocked什么东西来防止缓存?
编辑1
Interlocked.Exchange和Volatile.Write有什么区别?
两种方法都更新某些变量的值.有人可以总结何时使用它们?
http://msdn.microsoft.com/ru-ru/library/bb337971 和http://msdn.microsoft.com/en-us/library/gg712713.aspx
特别是我需要更新我的数组的双项,我希望另一个线程看到最新的值.什么是首选?Interlocked.Exchange(ref arr[3], myValue)或Volatile.Write(ref arr[3], info);在那里arr被声明为double?
================================================== ==========================真实的例子,我声明了这样的双数组:
private double[] _cachedProduct;
Run Code Online (Sandbox Code Playgroud)
在一个线程中,我更新它:
_cachedProduct[instrumentId] = calcValue;
...
are.Set();
Run Code Online (Sandbox Code Playgroud)
在另一个线程中,我像这样读取这个数组:
while(true) {
are.WaitOne();
...
result += _cachedProduct[instrumentId];
...
}
Run Code Online (Sandbox Code Playgroud)
对我来说它只是工作正常.然而,为了确保"它将永远有效",无论它看起来我应该添加Volatile.Write或Interlocked.Exchange.因为双重更新不能保证是原子的http://msdn.microsoft.com/en-us/library/aa691278%28VS.71%29.aspx
在这个问题的答案中,我希望看到Volatile和Interlocked类的详细比较.为什么我们需要2节课?哪一个和何时使用?
我读过很多关于volatile和VoletileRead(ReadAcquireFence)的矛盾信息(msdn,SO等).
我理解那些内存访问重新排序的限制含义 - 我仍然完全混淆的是新鲜度保证 - 这对我来说非常重要.
(...)这可确保始终在字段中显示最新值.
读取volatile字段称为volatile读取.易失性读取具有"获取语义"; 也就是说,保证在指令序列之后发生的任何内存引用之前发生.
public static int VolatileRead(ref int address)
{
int ret = address;
MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
return ret;
}
Run Code Online (Sandbox Code Playgroud)
根据msdn MemoryBarrier doc内存屏障阻止重新排序.然而,这似乎对新鲜度没有任何影响 - 对吗?
如何获得新鲜度保证?标记字段volatile与使用VolatileRead和VolatileWrite语义访问它之间有区别吗?我目前正在执行我的性能关键代码,需要保证新鲜度,但读者有时会得到陈旧的价值.我想知道是否标记状态不稳定将使情况不同.
EDIT1:
我正在努力实现的目标 - 保证读者线程将尽可能获得共享变量(由多个编写者编写)的最新值 - 理想情况下不会超过上下文切换或其他可能推迟立即操作的操作的成本写国家.
如果挥发性或更高级别的构造(例如锁定)具有这种保证(是吗?)而不是它们如何实现这一点?
EDIT2:
非常简洁的问题应该是 - 如何在读取过程中尽可能保证新的价值?理想情况下没有锁定(因为不需要独占访问,并且存在高争用的可能性).
从我在这里学到的东西,我想知道这可能是解决方案(求解(?)行标有注释):
private SharedState _sharedState;
private SpinLock _spinLock = new SpinLock(false);
public void Update(SharedState newValue) …Run Code Online (Sandbox Code Playgroud) private InstrumentInfo[] instrumentInfos = new InstrumentInfo[Constants.MAX_INSTRUMENTS_NUMBER_IN_SYSTEM];
public void SetInstrumentInfo(Instrument instrument, InstrumentInfo info)
{
if (instrument == null || info == null)
{
return;
}
instrumentInfos[instrument.Id] = info; // need to make it visible to other threads!
}
public InstrumentInfo GetInstrumentInfo(Instrument instrument)
{
return instrumentInfos[instrument.Id]; // need to obtain fresh value!
}
Run Code Online (Sandbox Code Playgroud)
SetInstrumentInfo并GetInstrumentInfo从不同的线程调用.
InstrumentInfo是不可变的类.我保证在打电话时有最新的副本GetInstrumentInfo吗?我担心我可以收到"缓存"副本.我应该添加同步吗?
声明instrumentInfos是volatile不会帮助,因为我需要声明数组项为volatile,不是数组本身.
我的代码有问题,如果有问题如何解决?
UPD1:
我需要我的代码在现实生活中工作,不符合所有规范!因此,如果我的代码在现实生活中起作用,但在某些环境下的某些计算机上"理论上"不起作用 - 那没关系!
Thread.MemoryBarrier,除了增加延迟之外什么都不做.我认为我们可以相信微软将在未来版本中继续使用"强大的内存模型".至少微软不太可能改变内存模型.所以我们假设它不会.UPD2: …
我有一个程序,它使用线程顺序执行耗时的进程.我希望能够监视每个线程的进度,类似于BackgroundWorker.ReportProgress/ ProgressChangedmodel的方式.我不能使用ThreadPool或BackgroundWorker由于我的其他限制.允许/公开此功能的最佳方法是什么.重载Thread该类并添加属性/事件?另一个更优雅的解决方案?