引用字段需要是易变的,如果我在锁期间修改它
本文关键字:修改 如果 字段 易变 引用 | 更新日期: 2023-09-27 18:01:33
考虑以下发生在后台线程("线程B")中的代码:
List<T> invocationQueueCopy;
lock (invocationQueue)
{
invocationQueueCopy = invocationQueue;
invocationQueue = new List<T>();
}
在另一个线程("线程A")中,我只是在添加之前锁定"invocationQueue":
lock (invocationQueue)
{
invocationQueue.Add(args);
}
我已经读到引用赋值是原子的,但是是否会发生"线程A"将在收到锁后最终写入旧列表(在"线程B"中替换的列表)?我读过其他答案,暗示它可以,如果引用的值存储在"线程a"的寄存器中,那么它就不知道"线程B"已经修改了类中的值。如果是这样,声明"invocationQueue"易失性会防止这种情况吗?
指出:
- 我知道我可以克隆然后清除列表。
- 我我知道我可以有一把单独的锁
但我宁愿不做这两件事,除非它是必要的。
提前感谢。
编辑:从Adam的注释中澄清一下:invocationQueue是一个私有字段,它是在这个类的内部创建的,从来没有暴露给外部世界,所以除了这两个方法之外,没有什么可以锁定它。
EDIT:您的解决方案将起作用。Lock创建了一个完整的栅栏,因此可以防止任何缓存,这基本上意味着您将始终获得列表引用的最新值。正如评论中建议的那样,唯一的事情是应该锁定一个中性对象,而不是列表本身。
下面是错误的!!但是我把它放在这里是为了展示线程是多么的难…事实是,下面的推理被锁创建一个完整的栅栏的事实打败了。
是的,这可能发生,所以不要这样做这样。
即使你做了也不会变好锁成只读之类的对象。
看看会发生什么(尽管大多数
线程a和线程b正在执行不同的处理器,每个都有它自己的缓存存储参考incovationQueue.
- ThreadB锁invocationQueue,锁是做给一个引用的取为processor1的缓存,而不是
- ThreadB复制invocationQueue.
- ThreadA锁invocationQueue,锁是做给一个引用的为processor2和上的缓存所取在这一刻,哪个是一样的processor1中的那个,然后启动等待。
- ThreadB创建一个新的List,并将其分配给invocationQueue处理器1中的缓存被更新,但是因为变量不是易失的这就是所发生的一切。
ThreadA进入锁并从他的缓存中获取引用,该缓存指向对旧的参考,因此你最后将变量添加到旧的列表。所以你需要使列表易变如果你要去的话就用锁摆弄引用本身