Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/262.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么我们需要Thread.MemoryBarrier()?_C#_.net_Multithreading_Thread Safety_Memory Barriers - Fatal编程技术网

C# 为什么我们需要Thread.MemoryBarrier()?

C# 为什么我们需要Thread.MemoryBarrier()?,c#,.net,multithreading,thread-safety,memory-barriers,C#,.net,Multithreading,Thread Safety,Memory Barriers,在“简而言之,C#4”中,作者展示了这个类有时可以在没有内存载体的情况下编写0,尽管我无法在Core2Duo中复制: public class Foo { int _answer; bool _complete; public void A() { _answer = 123; //Thread.MemoryBarrier(); // Barrier 1 _complete = true; //

在“简而言之,C#4”中,作者展示了这个类有时可以在没有
内存载体的情况下编写0,尽管我无法在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);
}
这种需要对我来说似乎很疯狂。我如何识别所有可能发生这种情况的情况?我认为,如果处理器改变了操作顺序,它需要保证行为不会改变


你费心使用屏障吗?

如果你使用
volatile
lock
,内存屏障是内置的。但是,是的,你确实需要它。话虽如此,我怀疑您需要的数量是示例所示数量的一半。

复制多线程错误非常困难-通常您必须多次(数千次)运行测试代码,并进行一些自动检查,以便在错误发生时进行标记。您可以尝试在一些行之间添加一个短线程。Sleep(10),但它并不总是保证您会遇到与没有它时相同的问题


内存障碍是为那些需要对多线程代码进行真正核心的低级性能优化的人引入的。在大多数情况下,使用其他同步原语(即volatile或lock)会更好。

如果您接触来自两个不同线程的数据,可能会发生这种情况。这是处理器用来提高速度的诀窍之一——你可以构建不这样做的处理器,但它们会慢得多,所以没有人再这样做了。你可能应该读一些类似于识别各种比赛条件的东西

我总是使用某种更高级别的工具,如监视器或锁,但在内部,它们正在执行类似的操作,或者使用屏障来实现。

第一个任务很可能在第二个任务开始运行时完成。只有当两个线程同时运行该代码并且没有中间的缓存同步操作时,才能观察到这种行为。在您的代码中有一个,StartNew()方法将在线程池管理器中的某个位置获得一个锁

让两个线程同时运行此代码非常困难。此代码在几纳秒内完成。你必须尝试数十亿次,引入可变延迟,才能有任何胜算。当然,这并不是很重要,真正的问题是当这种情况随机发生时,你并不期待它


远离这一点,使用lock语句编写合理的多线程代码。

您将很难重现此错误。事实上,我甚至会说,您永远无法使用.NET框架复制它。原因是微软的实现使用了一个强大的内存模型进行写操作。这意味着写操作被视为不稳定的。易失性写入具有锁释放语义,这意味着所有先前的写入必须在当前写入之前提交

然而,ECMA规范的内存模型较弱。因此,从理论上讲,Mono甚至未来版本的.NET Framework可能会开始出现这种错误行为

所以我想说的是,消除障碍1和障碍2不太可能对项目的行为产生任何影响。当然,这不是保证,而是基于CLR当前实施情况的观察

消除障碍3和障碍4肯定会产生影响。这实际上很容易复制。好的,不是这个例子本身,但是下面的代码是一个比较有名的演示。它必须使用发布版本编译并在调试器外部运行。错误在于程序没有结束。您可以通过在
while
循环中调用
Thread.MemoryBarrier
或将
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();
    }
}
某些线程错误很难重现的原因是,您用于模拟线程交错的相同策略实际上可以修复该错误<代码>线程。睡眠
是最显著的例子,因为它会产生内存障碍。您可以通过在
while
循环中调用并观察bug是否消失来验证这一点


你可以从你引用的那本书中看到我对这个例子的另一个分析的答案。

我只引用一篇关于多线程的伟大文章:

考虑以下示例:

如果方法A和B在不同的线程上并发运行,可能是 B是否可能写入“0”?答案是肯定的——针对以下几点 原因:

编译器、CLR或CPU可能会将程序指令重新排序到 提高效率。编译器、CLR或CPU可能会引入缓存 优化,使变量的赋值对用户不可见 其他线程马上。C#和运行时非常小心地 确保这样的优化不会破坏普通的单线程 正确使用锁的代码或多线程代码。外面 在这些场景中,您必须通过 创建内存屏障(也称为内存围栏)以限制 指令重新排序和读/写缓存的影响

全围栏

最简单的记忆屏障是完整的记忆 阻止任何类型指令重新排序的屏障(全围栏) 或者在篱笆周围。调用Thread.MemoryBarrier会生成 全围栏;我们可以通过应用四个完整的围栏来修复我们的示例 如下:


这里很好地描述了
Thread.MemoryBarrier
背后的所有理论,以及为什么我们需要在非阻塞场景中使用它以使代码安全和健壮:

是的,作者说屏障2e3只是为了保证如果A在B之前运行,B如果需要无锁策略,则输入理论上需要的所有参数。@Brian:1和4是必需的。我不太确定2和3。看起来好像
class Foo
{
  int _answer;
  bool _complete;

  void A()
  {
    _answer = 123;
    _complete = true;
  }

  void B()
  {
    if (_complete) Console.WriteLine (_answer);
  }
}
class Foo
{
  int _answer;
  bool _complete;

  void A()
  {
    _answer = 123;
    Thread.MemoryBarrier();    // Barrier 1
    _complete = true;
    Thread.MemoryBarrier();    // Barrier 2
  }

  void B()
  {
    Thread.MemoryBarrier();    // Barrier 3
    if (_complete)
    {
      Thread.MemoryBarrier();       // Barrier 4
      Console.WriteLine (_answer);
    }
  }
}