Jal*_*aid 9 c# memory-management volatile nonblocking memory-barriers
为了实现多线程应用程序的无锁代码,我使用了volatile
变量,
理论上:该volatile
关键字仅用于确保所有线程都能看到volatile变量的最新值; 因此,如果线程A
更新变量值并且线程B
在该更新发生之后读取该变量,它将看到最近从线程A写入的最新值.正如我在Nutshell书中的C#4.0中读到的那样,这是不正确的,因为
应用volatile不会阻止写入后读取交换.
可以通过Thread.MemoryBarrier()
在每次获取volatile
变量之前放置来解决这个问题:
private volatile bool _foo = false;
private void A()
{
//…
Thread.MemoryBarrier();
if (_foo)
{
//do somthing
}
}
private void B()
{
//…
_foo = true;
//…
}
Run Code Online (Sandbox Code Playgroud)
如果这解决了问题; 考虑我们有一个while循环,它依赖于其中一个条件的值; Thread.MemoryBarrier()
在while循环之前放置是解决问题的正确方法吗?例:
private void A()
{
Thread.MemoryBarrier();
while (_someOtherConditions && _foo)
{
// do somthing.
}
}
Run Code Online (Sandbox Code Playgroud)
为了更准确,我希望_foo
变量在任何时候任何线程要求它时给出最新的值; 因此,如果Thread.MemoryBarrier()
在调用变量之前插入修复问题,那么我可以使用Foo
属性而不是在该属性的get中_foo
执行以下操作Thread.MemoryBarrier()
:
Foo
{
get
{
Thread.MemoryBarrier();
return _foo;
}
set
{
_foo = value;
}
}
Run Code Online (Sandbox Code Playgroud)
"C#In a Nutshell"是正确的,但它的说法没有实际意义.为什么?
让我们澄清一下.拿你原来的代码:
private void A()
{
//…
if (_foo)
{
//do something
}
}
Run Code Online (Sandbox Code Playgroud)
如果线程调度程序已经检查了_foo
变量会发生什么,但它会在//do something
注释之前暂停?好吧,那时你的另一个线程可能会改变它的值_foo
,这意味着你所有的挥发物和Thread.MemoryBarriers都没有计算!如果do_something
值为_foo
false 是绝对必要的,那么你别无选择,只能使用锁.
但是,如果do something
在突然_foo
变为false 时执行是正常的,那么这意味着volatile关键字足以满足您的需求.
要明确:告诉你使用记忆障碍的所有响应者都是不正确的或提供过度杀伤力.
这本书是正确的.
CLR的内存模型表明可以重新排序加载和存储操作.这适用于易失性和非易失性变量.
将变量声明为volatile
仅表示加载操作将具有获取语义,并且存储操作将具有释放语义.此外,编译器将避免执行某些优化,这些优化继承了以序列化的单线程方式访问变量的事实(例如,从循环中提升加载/存储).
volatile
单独使用关键字不会创建关键部分,也不会导致线程彼此神奇地同步.
编写无锁代码时应该非常小心.没有什么简单的事情,甚至专家都很难做到正确.
无论你想要解决的是什么原始问题,都可能有更合理的方法来实现它.
归档时间: |
|
查看次数: |
4056 次 |
最近记录: |