同步对象可以缓存吗?
我是多线程世界的新手,并开始涉足它。我发现线程需要同步。挥发性不再是可靠的东西。我想知道同步对象是否可以由编译器或在任何阶段缓存?
使用的平台/语言:c++、win32、Windows
在 c++ 中,volatile 关键字用于无法被 CPU 缓存的对象。但今天的编译器并不严格遵循这一点。是否有其他方法可以使同步对象不可缓存(或者其他优化不适用于这些对象)。
tl;dr:同步对象可以缓存吗?如果是,如何使其不可缓存?
I am new to multithreading world and started getting into it. I found threading requires synchronization. Volatile is no more a reliable thing. I would like to know if synchronization object are cacheable by compiler or at any stage?
Platform/languages used : c++, win32, Windows
In c++, volatile keyword is used for objects which can not be cached by CPUs. But today's compilers do not strictly follow this. Is there is other way around to make synchronization objects non-cacheable (or other optimizations are not applied on those objects).
tl;dr: Are synchronization objects cacheable? If yes, how can you make it non-cacheable ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我不确定我是否明白你的问题:编译器缓存几乎与多线程无关。编译器缓存唯一要做的就是通过缓存以前的编译来提高编译速度。
同步对象可以被“缓存”,因为它们是您决定用于同步的任意对象,但这对并发性影响不大。同步时唯一需要关心的是,当多个线程争用资源时,它们必须在同一个对象上同步,才能获得对该资源的读/写访问权限。
根据您提到的 易失性,我将进行大胆猜测,并假设您担心同步对象可能缓存在线程的本地缓存中,并且一个线程对同步对象的更改可能对另一个线程不可见线。然而,这是一个有缺陷的想法:
通常,您应该使用不会更改的同步对象(最好是只读、常量或最终对象),我们在这里只讨论引用,而不是对象本身的内容。这是一个例子:
对象同步=新对象();
字符串某事=“你好”:
无效的ModifySomething()
{
sync = new object();// <-- 你永远不应该这样做!!
锁定(同步)
{
某事=GenerateRandomString();
}
一般来说
每次线程调用
ModifySomething
时,同步对象都会被一个新对象替换,并且线程永远不会在同一个对象上同步,因此可能会并发写入something
。I'm not sure I follow your question: compiler cache has almost nothing to do with multithreading. The only thing that a compiler cache would do is to increase your compilation speed by caching previous compilations.
Synchronization objects can be "cached" since they're any arbitrary object that you've decided to use for synchronization, but that has little effect on concurrency. The only thing that you need to care about when synchronizing is that when you have multiple threads contending for a resource, they must all synchronize on the same object in order to get read/write access to the resource.
I'm going to take a wild guess, based on your mentioning of volatile, and assume that you're worried a synchronization object may be cached in a thread's local cache and changes to the synchronization object from one thread may not be visible to another thread. This, however, is a flawed idea:
Generally, you should use a synchronization object that will not change (ideally a readonly, const or final) and we're only talking about the reference here, not the content of the object itself. Here is an example:
object sync = new object();
string something = "hello":
void ModifySomething()
{
sync = new object();// <-- YOU SHOULD NEVER DO THIS!!
lock(sync)
{
something = GenerateRandomString();
}
}
Now notice that every time a thread calls
ModifySomething
, the synchronization object will be replaced by an new object and the threads will never synchronize on the same object, therefore there may be concurrent writes tosomething
.如果不指定运行时环境,这个问题就没多大意义。
就 Java 而言,同步对象(用于同步的对象)就像任何其他对象一样。 对象是同步的目标,因此仅当变量包含同步时才需要
易失性
(适用于成员变量)对象可以改变。我会避免需要此类构造的程序设计。请记住(同样,在 Java 中),表达式的求值(通常是变量访问)导致要使用的同步对象。在这方面,本次评估与其他评估没有什么不同。
然而,归根结底,它只是以一种定义明确且表现良好的方式使用特定运行时/环境的同步工具。
快乐编码。
例如,Java 保证
synchronized(x) { foo }
(其中 x 是特定对象)将创建一个互斥的临界区域,在其中执行foo
。为此,它必须进行内部工作以确保簿记数据在所有处理器/缓存中正确刷新。然而,就使用同步构造而言,这些细节通常超出了运行时的范围。The question doesn't make much sense without specifying a run-time environment.
In the case of, Java, say, a synchronization object (an object used for synchronization) is just like any other object. The object is target of the synchronization, so
volatile
(which applies to member variables) is only needed if the variable containing the synchronization object can change. I would avoid a program design that needs such constructs.Remember (again, in Java), it is the evaluation of an expression -- generally a variable access -- that results in the synchronization object to use. This evaluation is no different than any other in this aspect.
At the end of the day, however, it is just using the synchronization tools of a particular run-time/environment in a manner in which they are well-defined and well-behaving.
Happy coding.
Java, for instance, guarantees that
synchronized(x) { foo }
, where x is a particular object, will create a mutually exclusive critical region in whichfoo
is executed. To do this it must do internal work to ensure the book-keeping data is flushed correctly across all processors/caches. However, these details are generally outside the scope of the run-time in terms of using the synchronization construct.同步对象必须由操作系统管理,操作系统还管理线程和缓存。因此,处理缓存是操作系统的责任。如果它以某种方式知道同步对象仅在单个 CPU 上使用(例如,因为它没有将第二个 CPU 分配给您的进程),则操作系统很可能会决定将同步对象保留在第一个 CPU 的缓存中。如果它需要在 CPU 之间共享,那么也会发生这种情况。
一个实际的结果是您将始终初始化同步对象。在 C++ 中,这是很自然的(构造函数负责处理),但在其他语言中,您必须显式地这样做。操作系统必须跟踪同步对象。
Synchronization objects are necessarily managed by the OS, which also manages threads and caches. Therefore, it's the OS responsibility to deal with caches. If it somehow knows that a synchronization object is used only on a single CPU (e.g. because it didn't allocate the second CPU to your process), the OS may very well decide to keep the synchronization object in the fist CPU's cache. If it needs to be shared across CPU's, then that will happen too.
One practical consequence is that you'll always initialize synchronization objects. In C++, that's natural (the constructor takes care of that) but in other languages you must explicitly do so. The OS has to keep track of the synchronization objects.