Fel*_*oto 47 .net c# multithreading thread-safety memory-barriers
在"C#4 in a Nutshell"中,作者表明这个类有时可以写0 MemoryBarrier但是我无法在我的Core2Duo中重现:
public class Foo
{
int _answer;
bool _complete;
public void A()
{
_answer = 123;
//Thread.MemoryBarrier(); // Barrier 1
_complete = true;
//Thread.MemoryBarrier(); // Barrier 2
}
public void B()
{
//Thread.MemoryBarrier(); // Barrier 3
if (_complete)
{
//Thread.MemoryBarrier(); // Barrier 4
Console.WriteLine(_answer);
}
}
}
private static void ThreadInverteOrdemComandos()
{
Foo obj = new Foo();
Task.Factory.StartNew(obj.A);
Task.Factory.StartNew(obj.B);
Thread.Sleep(10);
}
Run Code Online (Sandbox Code Playgroud)
这种需要对我来说似乎很疯狂.如何识别出现这种情况的所有可能情况?我认为如果处理器改变了操作顺序,它需要保证行为不会改变.
你还懒得使用障碍吗?
Bri*_*eon 66
你将很难再现这个bug.事实上,我会说你永远无法使用.NET Framework重现它.原因是Microsoft的实现使用强大的内存模型进行写入.这意味着写入被视为易失性.易失性写入具有锁定释放语义,这意味着必须在当前写入之前提交所有先前写入.
但是,ECMA规范的内存模型较弱.因此,从理论上讲,Mono甚至.NET Framework的未来版本可能会开始展示错误的行为.
所以我所说的是,删除障碍#1和#2不太可能对程序的行为产生任何影响.当然,这不是保证,而是基于CLR当前实施的观察.
消除障碍#3和#4肯定会产生影响.这实际上很容易重现.好吧,本身不是这个例子,但下面的代码是一个比较着名的演示.它必须使用Release版本进行编译,并在调试器之外运行.错误是程序没有结束.您可以通过调用循环Thread.MemoryBarrier内部while或标记stop为来修复错误volatile.
class Program
{
static bool stop = false;
public static void Main(string[] args)
{
var t = new Thread(() =>
{
Console.WriteLine("thread begin");
bool toggle = false;
while (!stop)
{
toggle = !toggle;
}
Console.WriteLine("thread end");
});
t.Start();
Thread.Sleep(1000);
stop = true;
Console.WriteLine("stop = true");
Console.WriteLine("waiting...");
t.Join();
}
}
Run Code Online (Sandbox Code Playgroud)
一些线程错误难以重现的原因是因为用于模拟线程交错的相同策略实际上可以修复错误.Thread.Sleep是最值得注意的例子,因为它会产生内存障碍.您可以通过在while循环内放置一个调用并观察该bug消失来验证这一点.
您可以在此处查看我的答案,以便对您引用的书中的示例进行另一次分析.
Han*_*ant 10
第二个任务甚至开始运行时,第一个任务完成的几率非常好.如果两个线程同时运行该代码并且没有中间缓存同步操作,则只能观察到此行为.在您的代码中有一个,StartNew()方法将在某个地方的线程池管理器中进行锁定.
让两个线程同时运行此代码非常困难.此代码在几纳秒内完成.您将不得不尝试数十亿次并引入可变延迟以获得任何赔率.当然没有太多指向,真正的问题是,当你不期望它时会随机发生这种情况.
远离这个,使用lock语句编写健全的多线程代码.
| 归档时间: |
|
| 查看次数: |
16645 次 |
| 最近记录: |