C# 如果我只有一个读写器,并且它';这不是时间限制吗?

C# 如果我只有一个读写器,并且它';这不是时间限制吗?,c#,concurrency,locking,C#,Concurrency,Locking,我有一个有用户界面的程序 当用户按下按钮时,程序将加载一些数据,这将花费一分钟左右的时间,因此界面自然无法更新 因此,我将执行以下操作-我有一个名为“hasload”的布尔变量,加载代码(在它自己的线程上工作)在完成时将其设置为true。界面将不时检查并停止显示“加载”屏幕 问题是-由于这是一个共享变量,您可能希望锁定它(或者使用读写器slim锁或其他东西)。锁显然只围绕共享变量(“hasload”)。然而,由于: 只有一个读者 只有一个作家 如果接口读取的“错误”值是可以的,因为它将在几分之一

我有一个有用户界面的程序

当用户按下按钮时,程序将加载一些数据,这将花费一分钟左右的时间,因此界面自然无法更新

因此,我将执行以下操作-我有一个名为“hasload”的布尔变量,加载代码(在它自己的线程上工作)在完成时将其设置为true。界面将不时检查并停止显示“加载”屏幕

问题是-由于这是一个共享变量,您可能希望锁定它(或者使用读写器slim锁或其他东西)。锁显然只围绕共享变量(“hasload”)。然而,由于:

  • 只有一个读者
  • 只有一个作家
  • 如果接口读取的“错误”值是可以的,因为它将在几分之一秒内再次检查

  • 我还需要锁吗?澄清-如果两个线程同时尝试读取数据,是否存在任何“数据损坏”警告或其他问题,或者可能发生的“最坏”情况是,它读取了一次错误的值,并在下一次正确拾取了该值?

    您需要将布尔值声明为volatile

    如MSDN所述:

    volatile关键字表示字段可能被修改 同时执行的多个线程。是的字段 声明的volatile不受编译器优化的影响 假设通过单个线程进行访问。这确保了 字段中始终存在最新值


    如果不将其声明为volatile,编译器可能会优化代码,使标记的更新对于其他线程永远都不可见。

    您不需要为示例锁定布尔值,但如果您希望生成代码注释以指示您选择不锁定的原因,因为您可能会重新访问此代码,并且对共享布尔值的“脏”读取有不同的期望。

    因为它是一种小于或等于CPU体系结构字长的数据类型,所以您没有必要担心是否会出现数据损坏问题。写入和读取将是原子的

    但这不是问题所在。问题在于,在某些情况下,该标志的读取器可能永远不会看到它更改为
    true
    值。这实际上很容易用下面的代码演示。您需要使用发行版配置编译它,并在没有附加调试器的情况下运行它。您可能会注意到,程序永远不会停止有效地演示错误

    // * Must be compiled as RELEASE and ran outside of a debugger.
    class Program
    {   
        // Decorate with volatile to change the behavior.
        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...");
    
            // The Join call should return almost immediately.
            // With volatile it DOES.
            // Without volatile it does NOT.
            t.Join();         
        }
    }
    
    在你的具体案例中,这可能实际上是没有意义的。原因是编写器是后台线程,而阅读器是UI线程。这在这里是至关重要的,因为内存屏障将保证在后台线程结束时提交写操作,这可能是在
    hasLoad
    设置为
    true
    之后立即完成的。在UI线程方面,消息泵本身可能会为您注入一个未知的内存屏障。因此,每次检查
    hasload
    的值时(可能使用某种计时器),您可能会得到最新的值

    不管怎样,如果它在没有锁或使用
    volatile
    的情况下工作,那么它这样做只是偶然的。帮你自己一个忙,采取适当的保护措施,把锁拿走


    更好的方法是,将
    任务
    与新的
    异步
    等待
    关键字结合使用。如果操作正确,甚至无需使用
    hasload
    标志即可完成此操作,而且看起来也会更优雅。

    锁将阻止对跨线程共享的资源执行并发操作。您认为可能需要锁定的跨线程共享的资源是什么?变量本身。对不起,我想我已经说清楚了。我会很快地回顾我的问题,只需使用a并等待它触发RunWorkerCompleted事件,它被称为“过时数据”,尽管这不太可能(取决于实际代码等),但您在形式上是正确的。这是错误的。这个操作实际上并不安全。这引起了一个有趣的阅读。这正是我想要的答案。非常感谢。