从实例化对象读取属性是否是线程安全的?

时间:2014-04-07 16:04:31

标签: c# thread-safety singleton

我可以安全地从多个线程读取实例化单例的IsBusy布尔属性,而不会导致线程安全问题,或者我已经处于危险水域?

2 个答案:

答案 0 :(得分:5)

除非您有任何记忆障碍,否则如果价值可能发生变化,您可能潜入危险水域。

让我们将其中的单例部分排除在等式之外,因为它不是特别相关。考虑这个简单的例子:

// Not safe!
public class BusyIndicator
{
    public bool IsBusy { get; set; }
}

现在,如果你有一个线程:

foo.IsBusy = true;
// do some work
foo.IsBusy = false;

和另一个线程,仅在IsBusy设置为true后启动,具有:

// Do some work
while (foo.IsBusy) { }

...那么第二个线程可能会永远旋转 ......没有内存障碍以确保它能够看到""第一个帖子的变化。

您可以使用锁定或使用Interlocked安全地实现此目的,例如

// Safe (in terms of memory barriers)
public class BusyIndicator
{
    private int busy = 0;

    public bool IsBusy
    {
        get { return Interlocked.CompareExchange(ref busy, 0, 0) == 1; }
        set { Interlocked.Exchange(ref busy, value ? 1 : 0); }
    }
}

答案 1 :(得分:0)

如果您按如下方式定义了属性IsBusy,那么您肯定会遇到线程安全问题:

public bool IsBusy { get; set; }

这里的一个重要问题是可以在设置值之前检查并更改值。这意味着你有竞争条件。解决这个问题的一种方法是:

// this is going to keep trying to lock until it succeeds, after which it 
//     will do whatever work needs done.
while (true) {
  bool lockObtained = false;
  lock (objectInstance) {
    if (!objectInstance.IsBusy) {
      lockObtained = true;
      objectInstance.IsBusy = true;
    }
  }

  if (lockObtained) {
    // good to go here

    // and don't forget to clear the IsBusy
    objectInstance.IsBusy = false;
    break;
  }
}

这使用内置的.NET锁定机制来锁定对象,以便您可以检查和设置,而无需担心中间的进程抢占。