如何使公共属性线程安全?

时间:2012-09-11 15:36:16

标签: c# .net multithreading thread-safety

由于某些原因,我正在实施BackgroundWorker替换,我必须实现以下公共属性:

public bool CancellationPending { get; private set; }
public bool IsBusy { get; private set; }
public bool WorkerReportsProgress { get; set; }
public bool WorkerSupportsCancellation { get; set; }

我确定你知道他们在BackgroundWorker中的目的是什么。所以它们可能被不同的线程访问/修改。我担心如何为多线程“保护”它们。我认为将它们声明为volatile就足够了,但volatile无法应用于自动属性。

我该怎么办?我应该为这些属性创建私有字段,并声明它们volatile吗?或者我应该在每个lockget块中使用set吗?

我认为这应该是非常常见的情况 - 使属性(最好是自动属性)是线程安全的。请注意,在此示例中,所有属性都是原子类型。

修改

澄清我需要的东西:我需要确保所有线程始终读取属性的最新值。请参阅:https://stackoverflow.com/a/10797326/1081467

再次,您是否建议使用volatilelock或其他任何内容?...当使用bool属性时,保证原子性,所以只留下第二个问题(阅读最新值),那么如何正确解决这个问题呢?当你拥有非原始类型的属性时呢?您是否在lockget块中添加了set

4 个答案:

答案 0 :(得分:6)

我想出了以下实现。请评论您是否认为这是一个最佳解决方案:

//========== Public properties ==================================================//

public bool CancellationPending { get { return _cancellationPending; } private set { _cancellationPending = value; } }

public bool IsBusy { get { return _isBusy; } private set { _isBusy = value; } }

public bool WorkerReportsProgress { get { return _workerReportsProgress; } set { _workerReportsProgress = value; } }

public bool WorkerSupportsCancellation { get { return _workerSupportsCancellation; } set { _workerSupportsCancellation = value; } }

//========== Private fields ==================================================//

private volatile bool _cancellationPending;
private volatile bool _isBusy;
private volatile bool _workerReportsProgress;
private volatile bool _workerSupportsCancellation;

推理:字段属于bool类型的事实确保了原子性,因此不需要lock。使它们volatile将确保任何线程将读取当前值 - 而不是缓存 - 以防另一个线程修改它。我认为这是volatile关键字的确切目的(仅限有效用途),对吗?

答案 1 :(得分:3)

public bool CancellationPending { get; private set; }
public bool IsBusy { get; private set; }
public bool WorkerReportsProgress { get; set; }
public bool WorkerSupportsCancellation { get; set; }
  

所以他们可能被不同的线程访问/修改

不,这只适用于CancellationPendingIsBusy,而不适用于其他人 他们都是布尔,保证是原子的。原子性就足够了。

原始Backgroundworker的所有属性都记录为线程安全 请参阅this page底部附近。

答案 2 :(得分:1)

虽然我认为有更好的选择,例如将任务与其他调度程序一起使用,如svick所述。

如果你想继续这条路径,你肯定应该使用锁定而不是易变的字段作为volatile does not do what you thinkOh and this guy said something about never making a volatile field...

取决于易失性,您可以使用您喜欢的同步原语(锁定,互斥锁,互锁,ReaderWriterLockSlim等等),具体取决于访问特征。

答案 3 :(得分:0)

一种简单的方法是为每个要保证线程安全的属性使用互斥对象。在get和set属性中,使用Monitor.Enter(declaredObjectMutext)和Monitor.Exit(declaredObjectMutex)。完成后,这将使您的属性成为线程安全的(所有get和set调用都将阻塞调用,直到完成任何其他线程)。

另一种选择是使用Interlocked Class,它允许线程安全地修改整数和bools。如果这就是您使用属性的全部内容,那么这是一个简单的解决方案。