如果我在锁定期间修改引用字段,是否需要保持易失性?

发布于 2024-11-19 08:37:20 字数 749 浏览 3 评论 0原文

考虑以下代码,该代码发生在后台线程(“线程 B”)中:

List<T> invocationQueueCopy;
lock (invocationQueue)
{
    invocationQueueCopy = invocationQueue;
    invocationQueue = new List<T>();
}

在另一个线程(“线程 A”)中,我只需在添加“invokingQueue”之前锁定它:

lock (invocationQueue)
{
    invocationQueue.Add(args);
}

我已经读到引用分配是原子的,但可以是否会发生“线程 A”在收到锁后最终写入旧列表(“线程 B”中替换的列表)?我读过其他答案,暗示它可以,如果引用的值存储在“线程 A”上的寄存器中,那么它不会知道“线程 B”已修改类中的值。如果是这样,声明“incallingQueue”易失性会阻止这种情况发生吗?

注意:

  • 我知道我可以克隆然后清除列表。
  • 我 知道我可以有一把单独的锁 列表的对象。

但除非有必要,否则我宁愿不做这两件事。

提前致谢。

编辑:

只是从 Adam 的评论中澄清:invocationQueue 是一个私有字段,它是在此类内部创建的,并且从未暴露给外界,因此除了这两个方法之外,没有任何东西可以锁定它。

Consider the following code, which takes place in a background thread ("thread B"):

List<T> invocationQueueCopy;
lock (invocationQueue)
{
    invocationQueueCopy = invocationQueue;
    invocationQueue = new List<T>();
}

In another thread ("thread A") I simply lock "invocationQueue" before adding to it:

lock (invocationQueue)
{
    invocationQueue.Add(args);
}

I have read that reference assignment is atomic, but could it ever occur that "thread A" will end up writing to the old list (the one replaced in "thread B") after receiving the lock? I have read other answers that imply it could, if the value of the reference were stored in a register on "thread A" then it would not know that "thread B" had modified the value in the class. If this is so, would declaring "invocationQueue" volatile prevent this?

Notes:

  • I know I could clone then clear the list.
  • I
    know I could have a separate lock
    object for the list.

But I'd rather not do either of these things unless it is required.

Thanks in advance.

Edit:

Just to clarify from Adam's comments: invocationQueue is a private field which is created internally to this class and never exposed to the outside world so nothing could lock on it except these two methods.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

眼泪淡了忧伤 2024-11-26 08:37:20

编辑:您的解决方案将会起作用。锁定会创建一个 完整栅栏,因此任何缓存都会被阻止,基本上意味着您将始终获取列表引用的最新值。正如评论中所建议的,唯一的事情是您应该对中立对象进行锁定,而不是列表本身。

以下是错误的!!但我还是把它放在这里是为了展示硬线程可能是多么的操蛋……事实上,下面的推理被锁创建了一个完整的栅栏这一事实所击败。

是的,这可能会发生,所以不要这样做
就这样。

即使你这样做了,情况也不会好转
锁定为只读
对象。

看看会发生什么(尽管大多数
暂时不会发生)。

ThreadA 和 ThreadB 正在执行
不同的处理器,每个处理器都有
它自己的高速缓冲存储器保存
参考 incovationQueue。

  • ThreadB 锁定 invokingQueue,锁定是针对一个引用,该引用是
    取为processor1的缓存,而不是
    到变量名。
  • ThreadB 复制调用队列。
  • ThreadA 锁定 invokingQueue,锁定是针对一个引用,该引用是
    用于处理器2上的缓存并且
    此时此刻,与
    处理器 1 中的那个,然后启动
    等待。
  • ThreadB 创建一个新的 List 并将其分配给调用队列,
    处理器 1 中的缓存已更新,但
    因为变量不是易失性的
    这就是所发生的一切。
  • ThreadA 进入锁并从他的缓存中获取引用,该引用指向
    到旧的参考,因此你
    最终将变量添加到旧变量中
    列表。

所以你需要使列表不稳定
如果你打算的话,请使用锁
尝试参考本身。

EDIT: Your solution will work. Lock creates a full fence so any caching is prevented, basically meaning you'll always get the most recent value for the list reference. The only thing, as suggested in the comments is the fact that you should do the locking on a neutral object, not the list itself.

The following is wrong!! But I let it here anyway to show how fu*** hard threading might be... the fact is the following reasoning is defeated by the fact that lock creates a full fence.

Yeah, it can happen so don't do it
that way.

It won't get better even if you did
the lock into a readonly whatever
object.

See what might happen (although most
of the time it WON'T happen).

ThreadA and ThreadB are executing on
different processors, each one with
its own cache memory which holds the
reference to incovationQueue.

  • ThreadB locks invocationQueue, the lock is done to a reference which is
    taken for the cache of processor1, not
    to a variable name.
  • ThreadB copies the invocationQueue.
  • ThreadA locks invocationQueue, the lock is done to a reference which is
    taken for the cache on processor2 and
    which, in this moment is the same as
    the one in processor1, and starts
    waiting.
  • ThreadB creates a new List and assigns it to the invocationQueue, the
    cache in the processor1 is updated but
    since the variable is NOT volatile
    that's all that happens.
  • ThreadA enters the lock and gets the reference from his cache, which points
    to the old reference, therefore you
    end up adding the variable to the old
    list.

So you need to make the list volatile
AND use the lock if you're going to be
playing with the reference itself.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文