【问题标题】:Do I need a lock if I only have a single reader/writer and it's not timecritical?如果我只有一个读取器/写入器并且不是时间紧迫的,我是否需要锁?
【发布时间】:2014-10-23 19:36:21
【问题描述】:

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

当用户按下按钮时,程序会加载一些数据,这需要一分钟左右,所以界面自然会更新失败。

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

问题是 - 由于这是一个共享变量,您可能想要锁定它(或使用读取器/写入器苗条锁或其他东西)。锁显然只是在共享变量(“hasLoaded”)周围。但是因为:

  1. 只有一个阅读器
  2. 作家只有一位
  3. 如果接口读取“错误”值没关系,因为它会在几分之一秒内再次检查

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

【问题讨论】:

  • 锁会阻止对跨线程共享的资源的并发操作。您认为可能需要锁定的线程之间共享哪些资源?
  • 变量本身。对不起,我以为我说清楚了。我会很快审查我的问题
  • 只需使用 BackgroundWorker 并等待它触发 RunWorkerCompleted 事件

标签: c# concurrency locking


【解决方案1】:

由于它是一种小于或等于 CPU 架构字长的数据类型,因此您无需担心是否会出现数据损坏问题。写入和读取将是原子的。

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

// * 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();         
    }
}

在您的具体情况下,这实际上可能没有实际意义。原因是 writer 是后台线程, reader 是 UI 线程。这在这里至关重要,因为内存屏障将保证在后台线程结束时提交写入,这可能是在hasLoad 设置为true 之后立即执行的。在 UI 线程方面,消息泵本身可能会注入您不知道的内存屏障。因此,每次您检查hasLoaded 的值时(可能使用某种计时器),您可能会得到最新的值。

无论如何,如果它在没有锁或使用volatile 的情况下工作,那只是偶然。帮自己一个忙,采取适当的保护措施,然后把锁拿走。

更好的是,将Task 与新的asyncawait 关键字结合使用。如果做得正确,这甚至不需要hasLoaded 标志就可以完成,而且它看起来也会更优雅。

【讨论】:

  • 读起来很有趣。几乎是我想要的答案。谢谢。
【解决方案2】:

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

【讨论】:

  • 这是错误的。该操作实际上并不安全。
【解决方案3】:

您需要将布尔值声明为 volatile。

如 MSDN 上所述:

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

如果您不将其声明为 volatile,编译器可能会优化您的代码,以使其他线程永远不会看到标志的更新。

【讨论】:

  • 它被称为“陈旧数据”,虽然不太可能(这取决于实际代码等),但您在形式上是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-11
  • 1970-01-01
  • 2016-07-24
  • 2017-06-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多