C# 如果在锁定期间修改引用字段,引用字段是否需要易变?
考虑在后台线程(“线程B”)中发生的以下代码: 我已经读到引用分配是原子的,但是在收到锁之后,“线程A”是否会最终写入旧列表(在“线程B”中替换的列表)?我读过其他答案,这些答案暗示,如果引用的值存储在“线程a”的寄存器中,那么它就不会知道“线程B”修改了类中的值。如果是这样,声明“invocationQueue”volatile是否可以防止这种情况 注:C# 如果在锁定期间修改引用字段,引用字段是否需要易变?,c#,multithreading,reference,locking,volatile,C#,Multithreading,Reference,Locking,Volatile,考虑在后台线程(“线程B”)中发生的以下代码: 我已经读到引用分配是原子的,但是在收到锁之后,“线程A”是否会最终写入旧列表(在“线程B”中替换的列表)?我读过其他答案,这些答案暗示,如果引用的值存储在“线程a”的寄存器中,那么它就不会知道“线程B”修改了类中的值。如果是这样,声明“invocationQueue”volatile是否可以防止这种情况 注: 我知道我可以克隆然后清除列表 我 我知道我可以有一把单独的锁 对象的列表 但除非有必要,否则我宁愿不做这两件事 提前谢谢 编辑: 只是想
- 我知道我可以克隆然后清除列表
- 我 我知道我可以有一把单独的锁 对象的列表
只是想从Adam的评论中澄清一下:invocationQueue是一个私有字段,它是在这个类内部创建的,从来没有对外公开过,因此除了这两个方法之外,没有任何东西可以锁定它。编辑:您的解决方案可以工作。锁会创建一个新的值,因此任何缓存都会被阻止,这基本上意味着您将始终获得列表引用的最新值。正如注释中所建议的,唯一的一件事是,您应该锁定一个中立对象,而不是列表本身 以下是错误的!!但无论如何,我把它放在这里是为了展示fu***硬线程可能是多么的。。。事实是,锁创建了一个完整的围栏,这一事实击败了以下推理 是的,这是可能发生的,所以不要这样做 那样 即使你做了,也不会好起来 锁定到只读文件中 反对 看看会发生什么(尽管大多数 当然这不会发生) ThreadA和ThreadB正在上执行 不同的处理器,每个都有 它自己的高速缓存,其中保存 对incovationQueue的引用
- ThreadB锁定调用队列时,对 用于processor1的缓存,而不是 对变量名进行修改
- ThreadB复制调用队列
- thread锁定调用队列,锁定是对 为processor2上的缓存和 在这一刻,这和 processor1中的一个,然后启动 等待
- ThreadB创建一个新列表并将其分配给调用队列 处理器1中的缓存已更新,但 因为变量不是易变的 这就是所发生的一切李>
- ThreadA进入锁并从缓存中获取引用,该缓存指向 到旧的参考,因此你 最后将变量添加到旧的 名单
代码非常糟糕,您需要一个单独的(不可修改、只读)对象作为监视器,并在其上使用
lock()
,而不是队列本身。+1到bestsss,但看起来您应该使用真正的队列,如queue,而不是尝试滚动自己的队列。您也可以使用ConcurrentQueue,但我通常更喜欢执行自己的显式线程同步,以将其保持在最佳状态minimum@best,这有点夸张。只要invocationQueue
是私有的,就没有实际的区别。这只是一个理论上的论点,即未来版本的List
可以开始执行lock(this)
。“不太可能。”亨克,你什么意思?你不能通过引用本身来保护对象引用,不管它是否私有。将锁放在它所保护的对象中是常见的错误之一。@Henk:同意最初的评论有点夸张。然而,@OP-我认为拥有显式锁对象通常是值得的,例如,在某个点上,甚至可以从参数构造函数分配私有列表,然后谁知道哪个代码可能引用它?此外,使用显式锁对象,通过变量名更容易表示预期的锁上下文,这在某些情况下可能很有用。是的,+1表示OP希望避免做的两件事。而且它看起来仍然不稳定,排队的项目正在被丢弃。他们最好不要太重要。@Jorge我想确定我理解了你的答案,然后再做标记。你是说只要我用lock(specialLockObject)替换lock(invocationQueue),我的代码就可以了(而且不会丢失任何项目)?是的,这正是我要说的:)问题是锁会建立一个完整的内存屏障,所以threadA保证获得调用队列的最实际值。
List<T> invocationQueueCopy;
lock (invocationQueue)
{
invocationQueueCopy = invocationQueue;
invocationQueue = new List<T>();
}
lock (invocationQueue)
{
invocationQueue.Add(args);
}