考虑以下代码,它发生在后台线程(“线程B”)中:
List<T> invocationQueueCopy;
lock (invocationQueue)
{
invocationQueueCopy = invocationQueue;
invocationQueue = new List<T>();
}
在另一个线程(“线程A”)中,我只需在添加之前锁定“invocationQueue”:
lock (invocationQueue)
{
invocationQueue.Add(args);
}
我已经读过引用赋值是原子的,但是在收到锁之后,“线程A”最终是否会写入旧列表(在“线程B”中替换的那个)?我已经阅读了其他暗示它可能的答案,如果引用的值存储在“线程A”的寄存器中,那么它就不会知道“线程B”修改了类中的值。如果是这样,会声明“invocationQueue”volatile会阻止这个吗?
注意:
但除非有必要,否则我宁愿不做其中任何一件事。
提前致谢。
修改
只是为了澄清Adam的评论:invocationQueue是一个私有字段,它是在这个类内部创建的,从不暴露给外部世界,因此除了这两种方法之外没有任何东西可以锁定它。
答案 0 :(得分:4)
编辑:您的解决方案可行。 Lock会创建一个full fence,因此阻止了任何缓存,这基本上意味着您将始终获得列表引用的最新值。唯一的事情,正如评论中所建议的那样,你应该对中立对象进行锁定,而不是对列表本身进行锁定。
以下是错误的!!但无论如何,我都让它在这里展示了如何进行硬线程化......事实是,锁定会创造一个完整的栅栏,这会导致以下推理失败。
是的,它可能发生,所以不要这样做 那样。
即使你这样做也不会好转 把它锁成一个只读的东西 对象
看看会发生什么(尽管大多数情况 它不会发生的时间。)
正在执行ThreadA和ThreadB 不同的处理器,每个处理器 它拥有自己的缓存内存 参考incovationQueue。
- ThreadB锁定invocationQueue,锁定是对一个引用进行的 取得处理器1的缓存,而不是 变量名。
- ThreadB复制invocationQueue。
- ThreadA锁定invocationQueue,锁定是对一个引用进行的 取得处理器2和缓存 在这一刻,它是一样的 处理器1中的那个,然后启动 等待。
- ThreadB创建一个新List并将其分配给invocationQueue,即 处理器1中的缓存已更新但是 因为变量不是易变的 就是这一切。
- ThreadA进入锁定并从其缓存中获取引用 以旧的参考,所以你 最后将变量添加到旧的 列表。
所以你需要使列表变得不稳定 如果你愿意,可以使用锁 玩弄参考本身。