c# - C# 线程真的可以缓存一个值并忽略其他线程对该值的更改吗?

标签 c# multithreading

这个问题不是关于竞争条件、原子性或为什么应该在代码中使用锁的问题。我已经知道了。

更新:我的问题不是“是否存在 volatile 内存的奇怪之处”(我知道它确实存在),我的问题是“.NET 运行时是否抽象了那么多,所以您永远不会看到它”。

参见 http://www.yoda.arachsys.com/csharp/threads/volatility.shtml 和关于 Is a string property itself threadsafe? 的第一个答案

(它们实际上是同一篇文章,因为一篇引用了另一篇。)一个线程设置了一个 bool 值,另一个线程一直循环读取该 bool 值——那些文章声称读取线程可能会缓存旧值并且永远不会读取新值值,因此您需要一个锁(或使用 volatile 关键字)。他们声称以下代码可能会永远循环。 现在我同意锁定变量是一种好习惯,但我不敢相信 .NET 运行时真的会像文章中声称的那样忽略内存值的变化。我理解他们关于 volatile 内存与非 volatile 内存的讨论,我同意他们在非托管代码中有一个有效的观点,但我不相信 .NET 运行时不会正确抽象离开,以便下面的代码执行您期望的操作。 这篇文章甚至承认代码“几乎肯定”会工作(尽管不能保证),所以我调用 BS。任何人都可以验证以下代码是否总是有效吗?有没有人能得到一个失败的案例(也许你不能总是重现它)?

class BackgroundTaskDemo
{
    private bool stopping = false;

    static void Main()
    {
        BackgroundTaskDemo demo = new BackgroundTaskDemo();
        new Thread(demo.DoWork).Start();
        Thread.Sleep(5000);
        demo.stopping = true;
    }

    static void DoWork()
    {
         while (!stopping)
         {
               // Do something here
         }
    }
}

最佳答案

要点是:它可能会工作,但不保证按规范工作。人们通常追求的是出于正确原因工作的代码,而不是靠编译器、运行时和 JIT 的侥幸组合工作,它们可能在框架版本、物理 CPU、平台,以及诸如 x86 与 x64 之类的东西。

理解内存模型是一个非常非常复杂的领域,我并不自称是专家;但是该领域真正的专家向我保证,您所看到的行为是无法保证的。

您可以发布尽可能多的工作示例,但不幸的是,除了“它通常有效”之外,这并不能证明什么。它当然不能证明它保证可以工作。只需要一个反例就可以反驳,但发现它是问题所在……

不,我手边没有。


用可重复的反例更新:

using System.Threading;
using System;
static class BackgroundTaskDemo
{
    // make this volatile to fix it
    private static bool stopping = false;

    static void Main()
    {
        new Thread(DoWork).Start();
        Thread.Sleep(5000);
        stopping = true;


        Console.WriteLine("Main exit");
        Console.ReadLine();
    }

    static void DoWork()
    {
        int i = 0;
        while (!stopping)
        {
            i++;
        }

        Console.WriteLine("DoWork exit " + i);
    }
}

输出:

Main exit

但仍在运行,满载 CPU;请注意,此时 stopping 已设置为 trueReadLine 使进程不会终止。优化似乎取决于循环内代码的大小(因此 i++)。它显然只适用于“发布”模式。添加 volatile 一切正常。

关于c# - C# 线程真的可以缓存一个值并忽略其他线程对该值的更改吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/458173/

相关文章:

c# - 如何将包含 3 个项目的元组列表分组,其中 element.item3 == element2.item2

c# - 如何在ItemsControl中隐藏最后一个子控件

java - 单线程和多线程程序的基准

java - 每个 api 请求中的线程池与单个应用程序池重用

java - 使用线程对象引用调用方法

c# - 从 Azure 存储部署云服务

c# - 我如何使用 NServiceBus 做竞争消费者

c# - 在 C# 中将 RGB 数组转换为图像

java - 线程完成时是否发出通知信号?为什么此代码示例有效?

c# - Visual Studio 2015 调试在多线程应用程序中不起作用