如果我只有一个读写器并且时间不重要,我是否需要锁定?

时间:2014-08-29 18:32:32

标签: c# concurrency locking

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

当用户按下按钮时,程序将加载一些数据,这将需要一分钟左右,因此接口自然无法更新。

所以,我正在做以下事情 - 我有一个名为" hasLoaded"的布尔变量。加载代码(处理它自己的线程)将在完成时设置为true。界面将经常检查并停止显示“正在加载”。屏幕。

问题是 - 因为这是一个共享变量,你可能想要锁定它(或使用读者/作者瘦锁或其他东西)。锁显然只是共享变量(" hasLoaded")。但是从那以后:

  1. 只有一位读者
  2. 只有一位作家
  3. 如果界面显示错误的'那就没问题。价值,因为它将在几分之一秒内再次检查
  4. 我还需要锁吗?澄清 - 是否有任何数据损坏'如果两个线程同时尝试读取它,或者是“最差”的问题,可能会发生警告或其他问题。可能发生的事情是它读取错误的值一次并在下一次正确地选择它?

3 个答案:

答案 0 :(得分: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();         
    }
}

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

无论如何,如果它没有锁定或使用volatile,它只是偶然地这样做。帮自己一个忙,采取适当的保护措施,然后锁定。

更好的是,将Task与新的asyncawait关键字结合使用。如果操作正确,即使不需要hasLoaded标志也可以完成,并且它看起来也会更优雅。

答案 1 :(得分:0)

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

如MSDN所述:

  

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

如果你没有将它声明为volatile,编译器可能会优化你的代码,使得另一个线程永远不会看到标志的更新。

答案 2 :(得分:-1)

您不需要锁定示例的布尔值,但是如果您可能想要进行代码注释以指示您选择不锁定的原因,那么您可能会重新访问此代码并对'提出不同的期望。脏'读取共享布尔值。