Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/290.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# 何时可以保证一个线程上更改的值对其他线程可见?_C#_.net_Multithreading_Memory Model_Memory Barriers - Fatal编程技术网

C# 何时可以保证一个线程上更改的值对其他线程可见?

C# 何时可以保证一个线程上更改的值对其他线程可见?,c#,.net,multithreading,memory-model,memory-barriers,C#,.net,Multithreading,Memory Model,Memory Barriers,我知道线程可以缓存一个值,但我想知道它的一个变体。一个线程是否可能更改一个缓存的值,而该值永远不会离开缓存,因此对其他线程永远都不可见 例如,此代码是否可以打印“Flag is true”,因为线程a从未看到线程b对Flag所做的更改?(我不能让它这样做,但我不能证明它或它的某些变体不会这样做。) 我可以想象,在线程bflag上,它可以缓存在CPU寄存器中,然后设置为false,当b进入而循环时,它永远没有机会(或根本不关心)将flag的值写回内存,因此,a始终将标志视为true 在我看来,从本

我知道线程可以缓存一个值,但我想知道它的一个变体。一个线程是否可能更改一个缓存的值,而该值永远不会离开缓存,因此对其他线程永远都不可见

例如,此代码是否可以打印“Flag is true”,因为线程
a
从未看到线程
b
Flag
所做的更改?(我不能让它这样做,但我不能证明它或它的某些变体不会这样做。)

我可以想象,在线程
b
flag
上,它可以缓存在CPU寄存器中,然后设置为
false
,当
b
进入
循环时,它永远没有机会(或根本不关心)将
flag
的值写回内存,因此,
a
始终将
标志
视为true


在我看来,从本文中列出的记忆屏障发生器来看,理论上是可能的。我说得对吗?我还没能在实践中证明这一点。有人能举一个这样的例子吗?

我不完全确定这是否能回答你的问题,但这里是

如果运行以下代码的发行版(非调试版),它将永远不会终止,因为
waitForFlag()
从未看到
flag
的更改版本

但是,如果您注释掉其中一行,程序将终止

这看起来像是在
while(flag)
循环中调用外部库会导致Optimizer不缓存
flag
的值

另外(当然)使
标志
不稳定将阻止这种优化

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    class Program
    {
        void run()
        {
            Task.Factory.StartNew(resetFlagAfter1s);
            var waiter = Task.Factory.StartNew(waitForFlag);

            waiter.Wait();
            Console.WriteLine("Done");
        }

        void resetFlagAfter1s()
        {
            Thread.Sleep(1000);
            flag = false;
        }

        void waitForFlag()
        {
            int x = 0;

            while (flag)
            {
                ++x;
                // Uncommenting this line make this thread see the changed value of flag.
                // Console.WriteLine("Spinning"); 
            }
        }

        // Uncommenting "volatile" makes waitForFlag() see the changed value of flag.
        /*volatile*/ bool flag = true;

        static void Main()
        {
            new Program().run();
        }
    }
}

研究Thread.MemoryBarrier,你将获得金牌。

从最容易正确操作到最容易出错

  • 读/写时使用锁
  • 使用
    联锁
    类上的功能
  • 使用记忆屏障
  • 一个线程是否可能更改一个缓存的值,而该值永远不会离开缓存,因此对其他线程永远都不可见

    如果我们说的是硬件缓存,那么我们需要讨论特定的处理器系列。如果您正在x86(和x64)上工作(看起来很可能),那么您需要知道,这些处理器实际上具有比.NET所需的更强的内存模型。在x86系统中,缓存保持一致性,因此其他处理器不能忽略任何写操作

    如果我们讨论的是优化,其中一个特定的内存位置已被读入处理器寄存器,然后从内存中进行的后续读取只是重用寄存器,那么在写端就没有类似的模拟。您会注意到,在我们假设没有其他东西在改变该内存位置之前,从实际内存位置至少读取一次,因此我们可以重用寄存器


    在写端,我们被告知将某些内容推送到特定的内存位置。我们必须至少推送到那个位置一次,并且总是将以前已知的值存储在一个单独的寄存器中(特别是如果我们的线程从未从中读取),这可能是一种非优化,以便能够执行比较并省略写入操作。

    @MatthewWatson,谢谢,这个链接回答了我最初提出的问题,但不是我想要找到的。我编辑了这个问题以反映这一点。我实际上是在追踪一个可以用我上面描述的来解释的bug,但是我想知道我的推理是否正确,以及这在现实中的可能性有多大。好的,我现在重新打开了这个问题,它已经被更改了。这个问题可以通过说明所涉及的编程语言来改进。从标签上看,我猜是C#。“我实际上在追踪一个bug…”-如果你能创建一个重新创建这个bug的程序,而不是试图帮助你证明否定,我们可能会更好地帮助你。这说明了我最初链接的问题中发生的情况,也就是说,一个线程缓存一个值,然后错过在另一个线程上对该值进行的更新。我想知道是否可能看到一个线程缓存一个值并修改其缓存的值,但这种变化在其他线程上从未出现过,不管它们是否重新读取该值。这几乎完全回答了这个问题,但我能澄清一些事情吗?“我们必须至少推一次到那个位置”——这个推操作是写操作的一部分吗?i、 这不是可以推迟的事情吗?@eoinmullan-是的,我只是想避免过度使用write。这不是拖延的事情。
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Demo
    {
        class Program
        {
            void run()
            {
                Task.Factory.StartNew(resetFlagAfter1s);
                var waiter = Task.Factory.StartNew(waitForFlag);
    
                waiter.Wait();
                Console.WriteLine("Done");
            }
    
            void resetFlagAfter1s()
            {
                Thread.Sleep(1000);
                flag = false;
            }
    
            void waitForFlag()
            {
                int x = 0;
    
                while (flag)
                {
                    ++x;
                    // Uncommenting this line make this thread see the changed value of flag.
                    // Console.WriteLine("Spinning"); 
                }
            }
    
            // Uncommenting "volatile" makes waitForFlag() see the changed value of flag.
            /*volatile*/ bool flag = true;
    
            static void Main()
            {
                new Program().run();
            }
        }
    }