我有一些多线程代码,我想增加一点的性能,所以我想知道我是否可以摆脱锁定。
我有一个现场成员:
private IList<ServerStatus> status;
它在类似的线程中更新:
status = GetUpdatedStatus();
它用在另一个这样的线程中:
var currentStatus = status;
所以问题是,如果没有锁定两个赋值语句,上面会产生任何问题吗?
我想我能看到的唯一场景是currentStatus为null,但是我再次希望赋值在某种程度上是线程安全的(或者它已经更改了引用)
答案 0 :(得分:20)
你是对的。您将看到作业,否则您将看不到它。引用的赋值(和读取)总是“原子的”(最后它是因为32位机器引用是32位,所以可以原子方式完成,而64位机器(运行64位应用程序)引用是64位,所以可以原子方式完成。唯一的例外是尝试在32位机器上写/读长(64位)。你必须使用Interlocked.Read / Interlocked.Exchange)
通常应将状态声明为volatile
,以便每个线程只能看到最新版本。你应该读到这个:http://www.albahari.com/threading/它非常好!
如果您不信任我,请阅读Do We Really Need Locks and Barriers?
此处http://www.albahari.com/threading/part4.aspx
The volatile keyword
部分,红色框下面的部分。 Notice that applying volatile doesn’t prevent a write followed by a read from being swapped, and this can create brainteasers
。最后,唯一可以确定的方法是使用Interlocked.Exchange
来编写和Interlocked.CompareExchange
来读取内容或者通过同步保护读取和写入部分(如lock
)或填写您的使用Thread.MemoryBarrier程序(但不要尝试,你会失败,你甚至不知道为什么)。保证锁定中的所有读取和写入都将在锁定中完成,而不是在锁定之前或之后。
答案 1 :(得分:6)
参考写入保证是原子的,因此只需要检查两件事:
volatile
Interlocked.CompareExchange
以确保不丢失数据;继续重新应用您的更改,直到您赢得交换即
object snapshot, newValue;
do
{
snapshot = field;
// do something based on that; create a clone
// with more/less data for example
newValue = ...;
} while (!ReferenceEquals(
Interlocked.CompareExchange(ref field, newValue, snapshot), snapshot));