一个C#线程可以真正缓存值,忽略的变化对其他线程的价值? [英] Can a C# thread really cache a value and ignore changes to that value on other threads?

查看:126
本文介绍了一个C#线程可以真正缓存值,忽略的变化对其他线程的价值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题的的有关种族条件,原子,或者为什么你应该在你的code使用锁。我已经知道这些。

更新:我的问题是不是做怪事挥发性记忆存在(我知道它),我的问题是不.NET运行时抽象的路程,所以你永远不会看到它。

请参阅http://www.yoda.arachsys.com/csharp/threads/volatility.shtml
和在第一解答<一href=\"http://stackoverflow.com/questions/434890/is-a-string-property-itself-threadsafe\">http://stackoverflow.com/questions/434890/is-a-string-property-itself-threadsafe

(他们真的在同一篇文章,因为一个引用除外)。一个线程设置一个布尔值,另一线环永远读取布尔 - 这些文章声称读线程可能会缓存旧的价值和没有读过新值,所以因此你需要一个锁(或使用volatile关键字)。他们声称以下code将有可能永远循环下去。
  现在,我同意这是很好的做法,锁定您的变量,但我无法相信.NET运行时真的会忽略一个内存值改变,因为文章称。我理解他们说说易失性存储器与非挥发性记忆体,我同意他们在的非托管的code之有理,但我不能相信.NET运行时不会正确的抽象即走,这样下code做你所期望的。
  文章甚至坦言code将几乎肯定的工作(尽管不能保证),所以我呼吁索赔学士学位。任何人都可以验证它是否是真的以下code不会永远工作吗?是任何人都能够得到甚至一案(也许你不能总是重现),其中失败?

 类BackgroundTaskDemo
{
    私人布尔停止=假;    静态无效的主要()
    {
        BackgroundTaskDemo演示=新BackgroundTaskDemo();
        新的Thread(demo.DoWork)。开始();
        Thread.sleep代码(5000);
        demo.stopping = TRUE;
    }    静态无效的DoWork()
    {
         而(!停)
         {
               //这里做什么
         }
    }
}


解决方案

的一点是:它可能工作,但它不是保证,以在规范工作。人们通常后code是正确的原因的作品,而不是由编译器,运行时和JIT,其中的的侥幸结合工作可能的框架版本之间的变化,物理CPU,平台,事情就是这样的x86 VS 64。

理解内存模型是一个非常非常复杂的领域,我不自称是专家;但谁是人在这方面真正的专家向我保证,你所看到的行为是不能保证。

只要你喜欢你可以张贴许多工作的例子,但不幸的是这并不能证明太多非它通常工作。这当然不能证明它的保证工作。这将只需要一个反例反驳,但发现它的问题...

不,我没有一个手。


更新与重复的反例:

 使用的System.Threading;
使用系统;
静态类BackgroundTaskDemo
{
    //使这种波动进行修复
    私人静态布尔停止=假;    静态无效的主要()
    {
        新的Thread(DoWork的)。开始();
        Thread.sleep代码(5000);
        停止=真;
        Console.WriteLine(主退出);
        到Console.ReadLine();
    }    静态无效的DoWork()
    {
        INT I = 0;
        而(!停)
        {
            我++;
        }        Console.WriteLine(DoWork的退出+ I);
    }
}

输出:

 主要出路

但仍然在运行,在满CPU;注意,停止已被这点设置为真正。在的ReadLine 是这样的过程不会终止。优化似乎是依赖于code的循环内的大小(因此我++ )。它只是在放模式下工作效果显着。添加挥发性,这一切工作正常。

This question is NOT about race-conditions, atomicity, or why you should use locks in your code. I already know about those.

UPDATE: My question isn't "does weirdness with volatile memory exist" (i know it does), my question is "doesn't the .NET runtime abstract that away so you'll never see it".

See http://www.yoda.arachsys.com/csharp/threads/volatility.shtml and the first answer on http://stackoverflow.com/questions/434890/is-a-string-property-itself-threadsafe

(They're really the same article since one references the other.) One thread sets a bool and the other thread loops forever reading that bool -- those articles claim the reading thread might cache the old value and never read the new value, so therefore you need a lock (or use the volatile keyword). They claim the following code will potentially loop forever. Now I agree it's good practice to lock your variables, but I can't believe the .NET runtime would really ignore a memory value changing as the article claims. I understand their talk about volatile memory vs non-volatile memory, and I agree they have a valid point in non-managed code, but I can't believe the .NET runtime won't correctly abstract that away so that the following code does what you expect. The article even admits the code will "almost certainly" work (though not guaranteed), so I'm calling BS on the claim. Can anyone verify that it's true the following code won't always work? Is anyone able to get even one case (maybe you can't always reproduce it) where this fails?

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
         }
    }
}

解决方案

The point is: it might work, but it isn't guaranteed to work by the spec. What people are usually after is code that works for the right reasons, rather than working by a fluke combination of the compiler, the runtime and the JIT, which might change between framework versions, the physical CPU, the platform, and things like x86 vs x64.

Understanding the memory model is a very very complex area, and I don't claim to be an expert; but people who are real experts in this area assure me that the behaviour you are seeing is not guaranteed.

You can post as many working examples as you like, but unfortunately that doesn't prove much other than "it usually works". It certainly doesn't prove that it is guaranteed to work. It would only take a single counter-example to disprove, but finding it is the problem...

No, I don't have one to hand.


Update with repeatable counter-example:

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);
    }
}

Output:

Main exit

but still running, at full CPU; note that stopping has been set to true by this point. The ReadLine is so that the process doesn't terminate. The optimization seems to be dependent on the size of the code inside the loop (hence i++). It only works in "release" mode obviously. Add volatile and it all works fine.

这篇关于一个C#线程可以真正缓存值,忽略的变化对其他线程的价值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆