如果我在锁定期间修改它,参考字段是否需要是易失性的?

时间:2011-07-09 11:39:57

标签: c# multithreading reference locking volatile

考虑以下代码,它发生在后台线程(“线程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是一个私有字段,它是在这个类内部创建的,从不暴露给外部世界,因此除了这两种方法之外没有任何东西可以锁定它。

1 个答案:

答案 0 :(得分:4)

编辑:您的解决方案可行。 Lock会创建一个full fence,因此阻止了任何缓存,这基本上意味着您将始终获得列表引用的最新值。唯一的事情,正如评论中所建议的那样,你应该对中立对象进行锁定,而不是对列表本身进行锁定。

以下是错误的!!但无论如何,我都让它在这里展示了如何进行硬线程化......事实是,锁定会创造一个完整的栅栏,这会导致以下推理失败。

  

是的,它可能发生,所以不要这样做   那样。

     

即使你这样做也不会好转   把它锁成一个只读的东西   对象

     

看看会发生什么(尽管大多数情况   它不会发生的时间。)

     

正在执行ThreadA和ThreadB   不同的处理器,每个处理器   它拥有自己的缓存内存   参考incovationQueue。

     
      
  • ThreadB锁定invocationQueue,锁定是对一个引用进行的   取得处理器1的缓存,而不是   变量名。
  •   
  • ThreadB复制invocationQueue。
  •   
  • ThreadA锁定invocationQueue,锁定是对一个引用进行的   取得处理器2和缓存   在这一刻,它是一样的   处理器1中的那个,然后启动   等待。
  •   
  • ThreadB创建一个新List并将其分配给invocationQueue,即   处理器1中的缓存已更新但是   因为变量不是易变的   就是这一切。
  •   
  • ThreadA进入锁定并从其缓存中获取引用   以旧的参考,所以你   最后将变量添加到旧的   列表。
  •   
     

所以你需要使列表变得不稳定   如果你愿意,可以使用锁   玩弄参考本身。