c# - 内存模型和线程池

标签 c# concurrency task volatile memory-model

我有一个类 NonVolatileTest :

public class NonVolatileTest 
{
    public bool _loop = true;
}

我有两个代码示例:

1:

private static void Main(string[] args)
{
    NonVolatileTest t = new NonVolatileTest();

    Task.Run(() => { t._loop = false; });

    while (t._loop) ;
    Console.WriteLine("terminated");

    Console.ReadLine();
}

2:

private static void Main(string[] args)
{
    NonVolatileTest t = new NonVolatileTest();

    Task.Run(() => { t._loop = false; });

    Task.Run(() =>
        {
            while (t._loop) ;
            Console.WriteLine("terminated");
        });

    Console.ReadLine();
}

在第一个示例中,所有工作都按预期进行,并且“while”循环永远不会终止,但在第二个示例中,所有工作据称“_loop”字段是易变的。

为什么?

附言。 VS 2013,.NET 4.5,x64 Release模式 & Ctrl + F5

假设:

这个“错误”可能与 TaskScheduler 有关。我认为,在JIT 将第二个任务编译运行之前,第一个任务已经完成,所以JIT 将更改后的值取走。

最佳答案

根据C# 5 specification (在带注释的 C# 4 规范中可以找到相同的段落),在第 10.5.3 节 - volatile 字段下,声明如下:

When a field-declaration includes a volatile modifier, the fields introduced by that declaration are volatile fields. For non-volatile fields, optimization techniques that reorder instructions can lead to unexpected and unpredictable results in multi-threaded programs that access fields without synchronization such as that provided by the lock-statement (§8.12). These optimizations can be performed by the compiler, by the run-time system, or by hardware. For volatile fields, such reordering optimizations are restricted:

(我的重点)

所以这被记录为不可预测的(也就是你无法控制的)。

这两段代码行为不同的事实可以归结为将代码提升到生成对象(用于闭包)的方法和不提升它之间的区别。


我的通灵密码阅读眼告诉我,这可能是第一种情况:

  1. 任务启动,但在调用委托(delegate)中的实际代码之前会产生开销
  2. 在调用委托(delegate)之前,主程序继续并设法启动循环,对控制变量进行一次读取,并继续重用其缓存的副本。
  3. 委托(delegate)最终会被执行,但这对循环没有影响,因为它已经读取了一次变量并且不想再次读取。

在第二种情况下,第一种情况有效地“通过一些对象引用读取变量”而第二种情况有效地“通过this 读取变量”这一事实稍微改变了上述情况> 引用”,可能会产生差异。

但真正的答案是您容易受到优化器的影响并且编写了不可预测的代码。

不要担心结果也是不可预测的。

对代码进行看似无关的微小更改可能会使优化器以不同的方式执行操作。

关于c# - 内存模型和线程池,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33280880/

相关文章:

visual-studio - 如何在 Visual Studio 中执行自定义文件特定命令/任务?

c# - 如果超时没有完成任务,Task.Wait(int) 是否会停止任务?

c# - 实现 ToArgb()

c# - InspirationContext 不持久化内容

c# - 如何允许多种类型的部署?

Java 线程池的使用

build - 在另一个项目中调用 gradle "build"任务

c# - 为什么 FTPWebRequest 或 WebRequest 通常不接受/../路径?

javascript - NodeJS 上的全局变量赋值是原子的吗?

concurrency - GPars 锁返回 null