我可以安全地从多个线程读取实例化单例的IsBusy
布尔属性,而不会导致线程安全问题,或者我已经处于危险水域?
答案 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锁定机制来锁定对象,以便您可以检查和设置,而无需担心中间的进程抢占。