关于线程同步的一些问题

Posted

技术标签:

【中文标题】关于线程同步的一些问题【英文标题】:Some questions about thread synchronization 【发布时间】:2010-11-18 13:29:53 【问题描述】:

我有一些问题想澄清一下。

    易失性阅读确保您阅读 变量的最新值。可以 意味着它强制所有CPU 为此刷新他们的缓存值 多变的?只有那个变量还是全部? 因此,如果它会强制所有 CPU 刷新缓存的写入并获取最新的 来自主内存的版本,这是一个 内存屏障?

    Volatile write 确保你写了一个 主变量的值 记忆。是否意味着它无效 所有的缓存值 所有 CPU 中的变量?

    您是否在使用内存屏障时 你用的是 volatile 关键字吗?

    Interlocked 执行 以原子方式读取/修改/写入 手术。互锁是否确保 例如,你是 递增 a 的最新版本 变量,其他 CPU 将看到 这个变化?我认为是因为它是 应该使用内存屏障, 但我不确定。那么我们可以说 那个 Interlocked 正在做一个 VolatileRead/修改/VolatileWrite 原子的?

    当您使用内存屏障时,是否 它影响到所有变量 CPUS,还是只是周围的?

    锁定是昂贵的,因为它 导致两个内存屏障和一个 “上下文切换”如果线程有 等待,但那是什么 互锁的优势?只为了 避免“上下文切换”?

    处理的是什么 ReaderWriterLockSlim 和 递归?我没看懂 有什么问题。

如你所见,我现在脑子里一片混乱。

提前致谢。

【问题讨论】:

这些都是很好的问题,但应该作为单独的帖子提出。我建议删除此帖子并将其拆分为几个较小的帖子。 我想我应该详细说明原因: 1. 长帖子不鼓励读者完成(因此您消除了潜在的回答者)。 2. 迁移者知道其中一个问题的答案,但不是全部,并且认为他们不应该发布不完整的答案(因此您消除了更多的回答者)。 3. 有很多问题的长问题需要很长时间才能回答(所以你会阻止更多的回答者)。如果你打破这篇文章,你会得到更好和更多的回应。 抱歉上面有错别字;将“迁移知道答案”替换为“可能知道答案”。 是的,你说得有道理。我正要这么做,但正如布赖恩温柔地回答的那样……我会离开的。下次我会记住你的建议。谢谢。 【参考方案1】:

在回答您的问题之前,我应该指出内存屏障不仅仅影响 CPU。当应用程序运行时,实际上有两种内存模型在起作用。一个是硬件级别,另一个是软件级别。作为开发人员,您必须针对两者中不同元素的最弱组合进行编码。对于 CLR 和 x86 架构,这通常意味着 CLR 更重要,因为 x86 架构实际上具有相当强大的内存模型。换句话说,volatile 关键字和其他内存屏障机制也会影响 JIT 生成代码的方式。

1.Volatile 读取确保您读取到变量的最新值。可以 意味着它强制所有CPU 为此刷新他们的缓存值 多变的?只有那个变量还是全部? 因此,如果它会强制所有 CPU 刷新 缓存写入并获取最新版本 从主内存,这是一个记忆 障碍?

首先,从技术上讲,易失性读取并不能确保您读取变量的最新值。它实际上意味着在 volatile 之前不能发生其他读取或写入。然而,效果是读取必须来自主内存如果它之前是另一个易失性读取。其次,不,易失性读取对其他写入没有影响,因此它不会强制所有 CPU 刷新其写入缓存。第三,是的,易失性操作被视为内存屏障。

2.Volatile write 确保您将值写入主内存中的变量。 这是否意味着它无效所有 该变量的所有缓存值 CPU?

与易失性读取类似,易失性写入在技术上与排序有关。它确保在 volatile 之后不会发生其他读取或写入。这并不意味着有问题的写入会立即被提交。它只影响执行该线程的 CPU。有趣的是,x86 架构实际上将 all 写入视为易失性。但是,CLR 没有(至少是 ECMA 规范)。这就是为什么您仍然必须在写入时使用 volatile 操作。这是从硬件和软件级别对最弱内存模型元素进行编码的一个示例。

3.你在使用关键字volatile时是否使用了内存屏障?

是的。有两种类型的内存屏障。全栅栏和半栅栏。半栅栏可以保证获取语义(易失性读取)或释放语义(易失性写入),但不能同时保证两者。一个完整的围栏(例如通过Thread.MemoryBarrier)可以保证两者。

4.Interlocked 在原子中执行读/修改/写 手术。互锁是否确保 例如,您正在递增 最新版本的变量和 其他 CPU 会看到这种变化吗?一世 这么想,因为它应该使用 记忆障碍,但我不确定。所以 我们可以说 Interloked 正在做 一个 VolatileRead/修改/VolatileWrite 原子的?

是的。对于值得联锁的递增和递减操作,可以使用CAS 操作来实现。在 .NET 中,您将在循环中使用 Interlocked.CompareExchange 方法,直到操作成功。我敢打赌Interlocked.IncrementInterlocked.Decrement 方法虽然使用的是本机CPU 指令,但我准备错了。

5.当你使用内存屏障时,它会影响所有变量吗? CPUS,还是只是周围的?

它将影响所有内存访问,但仅影响执行该线程的 CPU。

6.Locking 很昂贵,因为它会导致两个内存屏障和一个 “上下文切换”如果线程必须 等等,但那有什么好处 互锁的?只是为了避免 “上下文切换”?

基本上是的。线程永远不会因互锁操作而阻塞。

7.ReaderWriterLockSlim 和递归有什么关系? 我不明白这是什么问题。

你不能在同一个线程上获得两次锁而不首先释放它。 Joe Duffy's blog有更多信息。

【讨论】:

可惜我只能投你一次。我不明白为什么内存屏障只影响一个 CPU,其他 CPU 会发生什么?我的意思是,如果 CPU1 在其缓存中有一个变量的副本,并且我用 CPU2 的内存屏障写入该变量,那么您是否暗示 CPU1 不会看到更改,除非它也执行内存屏障?再次感谢,我欠你一品脱。 @bitbitbit:这正是我要说的!这就是为什么无锁编码策略很难正确实现的原因。【参考方案2】:

锁的一个问题是任何获得锁的线程都必须承担其他线程可能持有锁任意时间长度的风险。 Threading.Interlocked.CompareExchange 调用保证几乎立即返回(无论成功与否);除了在故意重度争用的情况下,short CompareExchange 自旋锁也将很快返回。如果一个线程获得一个锁,然后在释放它之前关闭并执行其他操作,那么其他线程将必须等待这些其他操作完成才能获得锁。 CompareExchange 自旋锁不存在这种危险。除非其他线程自己主动命中自旋锁,否则自旋锁将很快完成。

【讨论】:

以上是关于关于线程同步的一些问题的主要内容,如果未能解决你的问题,请参考以下文章

iOS--关于GCD的一些疑惑

归纳一下:C#线程同步的几种方法

[C++11 多线程同步] --- 线程同步概述

[C++11 多线程同步] --- 线程同步概述

[C++11 多线程同步] --- 线程同步概述

两个线程的公共资源——同步够不够?