问题标签 [memory-barriers]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
lock-free - 自旋锁总是需要内存屏障吗?在内存屏障上旋转是否昂贵?
我写了一些无锁代码,在大多数情况下都可以很好地处理本地读取。
本地旋转内存读取是否一定意味着我必须始终在旋转读取之前插入内存屏障?
(为了验证这一点,我设法生成了一个读写器组合,这导致在某些非常特定的条件下——专用 CPU、附加到 CPU 的进程、优化器一直打开、没有其他工作,读者永远看不到写入的值在循环中完成——所以箭头确实指向那个方向,但我不完全确定旋转通过内存屏障的成本。)
如果缓存的存储缓冲区中没有要刷新的内容,那么旋转通过内存屏障的成本是多少?即,所有过程正在做(在C中)是
我是否正确地假设它是免费的并且它不会用任何流量阻碍内存总线?
另一种说法是问:内存屏障除了刷新存储缓冲区、对其应用无效以及防止编译器在其位置重新排序读取/写入之外,还有什么作用吗?
反汇编, __sync_synchronize() 似乎转化为:
来自英特尔手册(对于新手来说同样模糊):
我的翻译:“当你说 LOCK 时,这会很昂贵,但我们只在必要时才这样做。”
@BlankXavier:
我确实测试过,如果写入器没有明确地从存储缓冲区中推出写入,并且它是该 CPU 上运行的唯一进程,那么读者可能永远看不到写入器的效果(我可以使用测试程序重现它,但是正如我上面提到的,它只发生在特定的测试中,具有特定的编译选项和专用的核心分配——我的算法工作正常,只有当我对它的工作原理感到好奇并编写了我意识到它可能具有的显式测试时一个问题在路上)。
我认为默认情况下简单的写入是 WB 写入(回写),这意味着它们不会立即被刷新,但读取将采用它们的最新值(我认为他们称之为“存储转发”)。所以我为作者使用了 CAS 指令。我在 Intel 手册中发现了所有这些不同类型的写入实现(UC、WC、WT、WB、WP),Intel vol 3A 第 11-10 章,仍在学习它们。
我的不确定性在于读者方面:我从 McKenney 的论文中了解到,还有一个失效队列,一个从总线到缓存的传入失效队列。我不确定这部分是如何工作的。特别是,您似乎暗示循环通过正常读取(即,非锁定,没有障碍,并且仅使用 volatile 以确保优化器在编译后离开读取)每次都会检查“无效队列” (如果存在这样的事情)。如果一个简单的读取不够好(即可以读取一个旧的缓存行,它仍然显示为有效等待队列失效(这对我来说也有点不连贯,但是失效队列是如何工作的呢?)),那么原子读取将是必要的,我的问题是:在这种情况下,这会对公共汽车有什么影响吗?(我认为可能不会。)
我仍在阅读英特尔手册,虽然我看到了关于存储转发的精彩讨论,但我还没有找到关于失效队列的很好讨论。我决定将我的 C 代码转换为 ASM 并进行实验,我认为这是真正了解其工作原理的最佳方式。
c++ - 没有 volatile 的互斥锁功能是否足够?
我和一位同事为在 x86、x64、Itanium、PowerPC 和其他 10 年历史的服务器 CPU 上运行的各种平台编写软件。
我们刚刚讨论了诸如 pthread_mutex_lock() ... pthread_mutex_unlock() 之类的互斥函数本身是否足够,或者受保护的变量是否需要是 volatile 的。
我关心的是缓存。编译器能否将 _protected 的副本放在堆栈或寄存器中,并在分配中使用该陈旧值?如果没有,是什么阻止了这种情况的发生?这种模式的变体是否容易受到攻击?
我假设编译器实际上并不理解 pthread_mutex_lock() 是一个特殊函数,所以我们只是受到序列点的保护吗?
非常感谢。
更新:好的,我可以看到解释为什么 volatile 不好的答案的趋势。我尊重这些答案,但关于该主题的文章很容易在网上找到。我在网上找不到的东西,以及我问这个问题的原因,是我如何在没有volatile 的情况下受到保护。如果上面的代码是正确的,它怎么能不受缓存问题的影响呢?
c# - 线程同步。锁定究竟如何使访问内存“正确”?
首先,我知道那lock{}
是Monitor
上课用的合成糖。(哦,语法糖)
我正在处理简单的多线程问题,发现无法完全理解如何锁定某些任意内存字来保护整个其他内存不被缓存是寄存器/CPU缓存等。使用代码示例来解释我在说什么更容易:
最终ms_Sum
将包含100000000
哪些,当然,这是预期的。
现在我们将在 2 个不同的线程上执行相同的循环,并且上限减半。
由于没有同步,我们得到了不正确的结果——在我的 4 核机器上,它几乎是随机数52 388 219
,略大于100 000 000
. 如果我们包含ms_Sum += 1;
在 中lock {}
,我们当然会得到绝对正确的结果100 000 000
。但是对我来说有趣的是(真的是说我期待类似的行为),添加lock
之前或之后的ms_Sum += 1;
行使答案几乎正确:
对于这种情况,我通常会得到ms_Sum = 99 999 920
,这是非常接近的。
问题:为什么确切地lock(ms_Lock) { ms_Counter += 1; }
使程序完全正确但lock(ms_Lock) {}; ms_Counter += 1;
几乎正确;锁定任意ms_Lock
变量如何使整个内存稳定?
非常感谢!
PS 去阅读有关多线程的书籍。
类似问题
memory-barriers - 内存屏障和宽松的内存模型
目前我试图提高我对内存屏障、锁和内存模型的理解。
据我所知,存在四种不同类型的放松,namley Write -> Read,Write -> Write,Read -> Write 和 Read -> Read。x86 处理器只允许写入->读取松弛,这通常称为总存储顺序 (TSO)。部分存储顺序 (PSO) 允许进一步的写入->写入松弛,而松弛存储顺序 (RSO) 允许上述所有松弛。
此外,还存在三种类型的内存屏障:释放、获取和两者一起使用。锁可以只使用获取和释放障碍,有时也可以使用完全障碍(.Net)。
现在考虑以下示例:
我目前的理解告诉我,如果我在 TSO 机器上运行此代码,我不需要额外的内存屏障。如果它是一台 PSO 机器,我需要在 x=1 和 flag = 1 之间设置一个释放屏障,以确保线程 1 在 flag =1 的情况下获得 x 的实际值。如果它是 RSO 机器,我需要在 while(flag != 1); 之间进一步设置一个获取障碍;并打印 x 以防止线程 1 提前读取 x 的值。
我的观察正确吗?
c++ - pthread_cond_signal 或 pthread_cond_broadcast 调用是否意味着写入内存屏障?
通常使用条件变量,以便在互斥锁下修改它们所指的状态。但是,当状态只是一个仅设置标志时,不需要互斥锁来防止同时执行。所以一个人可能想做这样的事情:
但是,这只有在pthread_cond_broadcast
意味着写入内存屏障时才是安全的;否则,等待线程可能会在标志写入之前看到条件变量广播。也就是说,等待线程可能会唤醒,消耗 cvar 信号,但仍然看到标志0
。
所以,我的问题是:pthread_cond_broadcast
andpthread_cond_signal
调用是否意味着写内存屏障?如果是这样,这在相关的 POSIX(或其他)规范中在哪里指定?规范在这一点上似乎不清楚。
注意:我知道,在实践中,这确实会导致内存屏障(在 Linux 上,因为线程唤醒意味着完整的 CPU 内存屏障,而跨库函数调用意味着编译器内存屏障)。但是,我对规范保证的内容感兴趣。
c# - 不同 CPU 内核上的上下文切换和线程执行
从我关于 SO 的另一个问题中,我发现它可能遵循简单的方法
如果在 if 和控制台 writeline 调用之间发生上下文切换,则可能在不同的 CPU 上执行。这对我来说是个新闻,所以我现在想知道何时可以为另一个 CPU 切换单线程代码,以及为什么在上述简单情况下它可能有意义?
c# - 试图理解 Thread.MemoryBarrier() 和上下文切换之间的关系
由于看起来上下文切换可能发生在指令执行的任何时候,我现在想知道为什么代码“部分有问题”(这两条指令)是有意义的,如果上下文切换可以在任何指令之间发生并且我们可能在不同的 CPU 上第二条指令的核心。
此处对 MemoryBarrier 的描述似乎并未保证MemoryBarrier在调用它后不会切换 CPU。
(这与这个问题有关)
c - 乱序执行和内存栅栏
我知道现代 CPU 可以乱序执行,但是它们总是按顺序退出结果,如 wikipedia 所述。
“Out of Oder 处理器及时用其他准备好的指令填充这些“插槽”,然后在最后对结果重新排序,以使指令看起来像正常处理。
现在据说在使用多核平台时需要内存栅栏,因为由于乱序执行,可以在此处打印错误的x值。
现在我的问题是,由于乱序处理器(我假设多核处理器的核心)总是按顺序退出结果,那么内存栅栏的必要性是什么。多核处理器的内核是否只看到从其他内核退出的结果,或者它们也看到正在进行的结果?
我的意思是在我上面给出的示例中,当处理器 2 最终将退出结果时,x的结果应该在f之前,对吗?我知道在乱序执行期间,它可能在x之前修改了f ,但它一定没有在x之前退休,对吧?
现在有了按顺序退出结果和缓存一致性机制,为什么在 x86 中还需要内存栅栏?
c++ - 内存模型排序和可见性?
我试着寻找这方面的细节,我什至阅读了关于互斥锁和原子的标准......但我仍然无法理解 C++11 内存模型可见性保证。据我了解,互斥互斥的非常重要的特性是确保可见性。也就是每次只有一个线程增加计数器是不够的,重要的是线程增加最后使用互斥锁的线程存储的计数器(我真的不知道为什么人们在讨论时不提这个互斥体,也许我有不好的老师:))。因此,据我所知, atomic 不会强制立即可见:(来自维护 boost::thread 并已实现 c++11 线程和互斥库的人):
带有 memory_order_seq_cst 的栅栏不会强制对其他线程立即可见(MFENCE 指令也不会)。C++0x 内存排序约束就是这样 --- 排序约束。memory_order_seq_cst 操作形成一个总顺序,但对该顺序没有任何限制,除非它必须得到所有线程的同意,并且不得违反其他顺序约束。特别是,线程可能会在一段时间内继续看到“陈旧”的值,前提是它们以与约束一致的顺序查看值。
我同意。但问题是我很难理解关于原子的 C++11 构造是“全局的”,并且只能确保原子变量的一致性。特别是我了解以下内存排序中的哪些(如果有)保证在加载和存储之前和之后会有一个内存栅栏:http: //www.stdthread.co.uk/doc/headers/atomic/memory_order。 html
据我所知,std::memory_order_seq_cst 插入了内存屏障,而其他只强制对某些内存位置的操作进行排序。
所以有人可以澄清一下吗,我想很多人会使用 std::atomic 制造可怕的错误,尤其是如果他们不使用默认值 (std::memory_order_seq_cst 内存排序)
2. 如果我是对的,这是否意味着此代码中的第二行是多余的:
3. std::atomic_thread_fences 是否具有与互斥锁相同的要求,从某种意义上说,为了确保非原子变量的 seq 一致性,必须执行 std::atomic_thread_fence(std::memory_order_seq_cst); 在加载和 std::atomic_thread_fence(std::memory_order_seq_cst) 之前;
商店之后?
4.是
相当于
我认为不会,但我想确定一下。
编辑:5.可以断言火吗?
只有两个线程存在。
第一个线程写入
第二个线程读取