C# 如果在锁定期间修改引用字段,引用字段是否需要易变?

C# 如果在锁定期间修改引用字段,引用字段是否需要易变?,c#,multithreading,reference,locking,volatile,C#,Multithreading,Reference,Locking,Volatile,考虑在后台线程(“线程B”)中发生的以下代码: 我已经读到引用分配是原子的,但是在收到锁之后,“线程A”是否会最终写入旧列表(在“线程B”中替换的列表)?我读过其他答案,这些答案暗示,如果引用的值存储在“线程a”的寄存器中,那么它就不会知道“线程B”修改了类中的值。如果是这样,声明“invocationQueue”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);
}