同步对象是否可缓存?

Are synchronization objects cacheable?

本文关键字:缓存 是否 对象 同步      更新时间:2023-10-16

我是多线程世界的新手,并开始进入它。我发现线程化需要同步。易失性不再是一个可靠的东西。我想知道如果同步对象是可缓存的编译器或在任何阶段?

使用平台/语言:c++, win32, Windows

在c++中,volatile关键字用于cpu无法缓存的对象。但是今天的编译器并不严格遵循这一点。是否有其他方法可以使同步对象不可缓存(或者没有对这些对象应用其他优化)

tl;dr:同步对象是否可缓存?如果是,如何使它不可缓存?

我不确定我是否遵循您的问题:编译器缓存几乎与多线程无关。编译器缓存唯一能做的就是通过缓存以前的编译来提高编译速度。

同步对象可以被"缓存",因为它们是您决定用于同步的任意对象,但这对并发性几乎没有影响。在同步时,您需要关心的唯一一件事是,当您有多个线程争用一个资源时,它们必须在同一个对象上同步,以便对资源进行读/写访问。

根据您提到的volatile,我将大胆猜测一下,并假设您担心同步对象可能缓存在线程的本地缓存中,并且一个线程对同步对象的更改可能对另一个线程不可见。然而,这是一个有缺陷的想法:
  1. 当你调用lock()或synchronize()(取决于语言)时,你所需要关心的是锁是在同一个对象上执行的,而不管对象的内部状态如何。
  2. 一旦你获得了一个对象上的锁,你在这个锁范围内修改的任何资源都只能被一个线程修改。
  3. 一般来说,您应该使用不会更改的同步对象(理想情况下是readonly、const或final),我们在这里只讨论引用,而不是对象本身的内容。下面是一个例子:

    object sync = new object();String something = "hello":

    空白ModifySomething (){sync = new object();//<——你不应该这样做!!锁(同步){something = GenerateRandomString();}}

现在请注意,每次线程调用ModifySomething时,同步对象将被一个新对象替换,并且线程永远不会在同一个对象上同步,因此可能并发写something

如果不指定运行时环境,这个问题就没有多大意义。


在Java的情况下,例如,同步对象(用于同步的对象)就像任何其他对象一样。对象是同步的目标,因此只有当包含同步对象变量可以更改时,才需要volatile(适用于成员变量)。我将避免需要这种构造的程序设计。

记住(同样是在Java中),是表达式的求值——通常是变量访问——导致要使用的同步对象。在这方面,这个计算和其他的没有什么不同。

然而,在一天结束时,它只是以一种定义良好且行为良好的方式使用特定运行时/环境的同步工具。

幸福的编码。


例如,Java保证synchronized(x) { foo }(其中x是一个特定的对象)将创建一个互斥的关键区域,在这个关键区域中执行foo。要做到这一点,它必须进行内部工作,以确保在所有处理器/缓存中正确刷新簿记数据。然而,就使用同步构造而言,这些细节通常超出了运行时的范围。

同步对象必须由操作系统管理,操作系统还管理线程和缓存。因此,处理缓存是操作系统的责任。如果它知道一个同步对象只在一个CPU上使用(例如,因为它没有分配第二个CPU给你的进程),操作系统很可能决定将同步对象保留在第一个CPU的缓存中。如果它需要跨CPU共享,那么也会发生。

一个实际的结果是,您将始终初始化同步对象。在c++中,这是很自然的(构造函数会处理这个问题),但在其他语言中,您必须显式地这样做。操作系统必须跟踪同步对象