当你遇到线程A读取一些全局变量并且线程B写入同一个变量的情况时,现在除非读/写在单个核心上不是原子的,否则你可以在没有同步的情况下进行,但是在运行时会发生什么一台多核机器?
答案 0 :(得分:9)
即使在单核上,也不能假设操作是原子的。这可能是你在汇编程序中进行编码的情况,但是,如果按照你的问题用C ++进行编码,你就不会知道它将编译成什么。
您应该依赖于您编码的抽象级别的同步原语。在你的情况下,这是线程调用C ++。无论是pthread,Windows线程还是其他东西。
这与我在whether i++ was thread-safe的另一个答案中提出的理由相同。最重要的是,您不知道,因为您没有编码到那个级别(如果您正在使用内联汇编程序和/或您理解并且可以控制在幕后发生的事情,那么您将不再编码C ++级别,你可以忽略我的建议。)
操作系统和/或操作系统类型的库对它们运行的环境有很多了解,远远超过C ++编译器。使用适当的同步原语将为您节省大量的烦恼。
答案 1 :(得分:7)
它将具有与单核相同的陷阱,但由于必须在核之间进行L1缓存同步而具有额外的延迟。
注意 - “你可以在不同步的情况下完成”并不总是真实的陈述。
答案 2 :(得分:5)
即使在单一机器上,如果没有明确的同步,也绝对无法保证这一点。
有几个原因:
如果要在两个线程之间进行正确的通信,则需要某种同步。 始终,否例外。
该同步可以是OS或线程API提供的互斥锁,也可以是CPU特定的原子指令,或者只是普通的内存屏障。
答案 3 :(得分:1)
对于多核计算机上的非原子操作,您需要使用系统提供的Mutex来同步访问。
对于C ++,boost mutex库提供了几种互斥类型,为OS提供的互斥锁类型提供了一致的接口。
如果您选择将boost视为同步/多线程库,则应阅读Synchronization概念。
答案 4 :(得分:0)
根据您的具体情况,以下内容可能相关。虽然它不会使你的程序运行不正确,但它可以在速度上产生很大的不同。即使您没有访问相同的内存位置,如果两个内核在缓存中的同一页面上颠簸(尽管因为您仔细同步数据结构而不是相同的位置),您可能会因缓存效应而受到性能影响。
这里有一个很好的“虚假分享”概述: http://www.drdobbs.com/go-parallel/article/showArticle.jhtml;jsessionid=LIHTU4QIPKADTQE1GHRSKH4ATMY32JVN?articleID=217500206
答案 5 :(得分:0)
就(新)C ++标准而言,如果程序包含数据争用,则程序的行为是未定义的。如果存在线程交错,则程序具有数据竞争,使得它包含来自不同线程的两个相邻冲突存储器访问(这是一种非常正式的方式,如果两个冲突的访问可以同时发生,则程序具有数据竞争“ )。
请注意,运行的核心数无关紧要,程序的行为是未定义的(特别是优化程序可以根据需要对指令进行重新排序)。
答案 6 :(得分:0)
没有人提到隐式同步的优点和缺点。
主要的“专业人士”当然是程序员可以写任何东西而不必费心同步。
主要的“骗局”是需要很多时间。隐式同步需要通过高速缓存,至少(您可能认为)两个核心共有的第一个高速缓存。错误!计算机中可能安装了多个物理处理器,因此同步不能在高速缓存中停止,它需要一直向下到RAM。如果要在那里进行同步,还需要与需要与任何总线主控设备的内存同步的其他设备进行同步。总线主控设备可能是经典PCI总线上的卡,并且可能以33 MHz运行,因此隐式同步也需要等待它们以确认可以写入或读取特定的RAM位置。我们说的是核心和最慢总线之间的时钟速度差异只有100倍,而最慢的总线需要几个自己的总线周期才能以可靠的方式做出反应。因为同步必须是可靠的,否则没有用。
因此,在为隐式同步实现电子设备(最好让程序员无论如何都要明确处理)和更快的系统之间进行选择时,可以在必要时进行同步,答案是显而易见的。
同步的显式键是LOCK前缀和XCHG mem,reg指令。
你可以说隐式同步就像训练轮子一样:你不会倒在地上,但你不能特别快速地转身或转得特别快。很快你就会厌倦,并希望继续前进。当然,你会受伤,但在这个过程中你要么学习要么退出。