|
|
1
7
问题1:使用“volatile”关键字
确切地。如果您不创建可移植代码,Visual Studio将以这种方式实现它。如果你想便携,你的选择目前是“有限的”。直到C++0X,没有可移植的方式如何指定具有保证读/写顺序的原子操作,并且需要实现每个平台解决方案。也就是说,Boost已经为你做了这件肮脏的工作,你可以利用 its atomic primitives . 问题2:变量需要4字节/8字节对齐?如果你让他们保持一致,你是安全的。如果不这样做,规则就很复杂(缓存行,…),因此最安全的方法是保持规则对齐,因为这很容易实现。 问题3:是否应该重写此代码以使用互斥体?关键部分是一个轻量级互斥体。除非需要在进程之间进行同步,否则请使用关键部分。 问题4:关键部分/互斥锁/信号量/旋转锁在哪里最适合?Critical sections 甚至可以 do spin waits 为你。 问题5:旋转锁不应在单芯中使用自旋锁使用这样一个事实:当等待的CPU旋转时,另一个CPU可能会释放该锁。这不能只在一个CPU上发生,因此这只是浪费时间。在多CPU上自旋锁是个好主意,但这取决于自旋等待成功的频率。这个想法是等待一小段时间要比在那里来回切换上下文快得多,因此如果等待时间可能很短,那么最好等待。 |
|
|
2
13
1)no volatile只是说每次仍有可能半更新时从内存重新加载值。 编辑: 2)Windows提供一些原子功能。查找 "Interlocked" functions . 这些评论让我做了更多的阅读。如果你通读 Intel System Programming Guide 您可以看到对齐的读和写是原子的。
8.1.1保证的原子操作
Intel486处理器(以及此后更新的处理器)保证
基本内存操作将始终以原子方式执行:
所以基本上是的,如果您从任何地址进行8位读/写,从16位对齐地址等进行16位读/写,您将得到原子操作。值得注意的是,您可以在现代计算机的缓存线内执行未对齐的内存读/写操作。规则看起来很复杂,所以如果我是你的话,我就不会依赖它们了。为评论家干杯,这对我来说是一次很好的学习经历。 3)关键部分将尝试为其锁旋转锁几次,然后锁定互斥锁。旋转锁定可以吸走CPU的能量而不做任何事情,而互斥体则需要一段时间来完成它的工作。如果不能使用互锁函数,那么关键部分是一个不错的选择。 4)选择一个选项而不是另一个选项会受到性能惩罚。这是一个非常大的要求,通过这里的一切好处。“msdn帮助”中有很多关于这些的好信息。我建议你读一下。 5)您可以在单线程环境中使用自旋锁,这通常不是必需的,尽管作为线程管理意味着您不能让两个处理器同时访问相同的数据。这是不可能的。 |
|
|
3
8
1:挥发性
本身
对于多线程来说几乎是无用的。它保证将执行读/写操作,而不是将值存储在寄存器中,并且保证读/写操作不会被重新排序。
关于其他
除此之外,对整数大小的对象的读/写在x86上是原子的,只要对象对齐良好。(你不能保证 什么时候? 不过,写入将发生。编译器和CPU可能会对其重新排序,因此它是原子的,但不是线程安全的) 2:是的,对象必须对齐才能使读/写成为原子的。 3:不是真的。只有一个线程可以执行代码 在给定的关键部分内 一次。其他线程仍然可以执行其他代码。所以可以有四个变量,每个变量都受不同的关键部分保护。如果它们都共享同一个关键部分,那么当您操作对象2时,我将无法操作对象1,这不仅效率低下,而且限制了并行性。如果它们受到不同关键部分的保护,我们就不能同时操纵 相同的 对象同时。 4:旋转锁 很少地 一个好主意。如果您希望线程在获得锁之前只需要等待很短的时间,那么它们非常有用, 和 你绝对需要最小的延迟。它避免了操作系统上下文切换,这是一个相对缓慢的操作。相反,线程只是位于一个循环中,不断地轮询一个变量。所以CPU使用率更高(在等待spinlock时,核心不会被释放以运行另一个线程),但线程将能够继续 尽快 当锁被释放时。 至于其他的,性能特征几乎是相同的:只使用最适合您需要的语义。通常,关键部分对于保护共享变量最为方便,互斥体可以很容易地用于设置“标志”,允许其他线程继续进行。 至于不在单核环境中使用spinlocks,请记住spinlock实际上不会产生。等待spinlock的线程A实际上并没有被挂起,这样操作系统就可以调度线程B运行。但是由于a正在等待这个spinlock,其他线程将不得不释放这个锁。如果您只有一个内核,那么另一个线程只能在A被关闭时运行。有了健全的操作系统,这迟早会发生,作为常规上下文切换的一部分。但既然我们知道A在B有时间执行之前无法获得锁,而且 释放 如果A立即生成,操作系统将其放入等待队列中,然后在B释放锁后重新启动,我们的情况会更好。这就是一切 其他 锁类型有。 旋转锁仍会 工作 在单核环境中(假设操作系统具有抢先的多任务处理),它的效率非常低。 |
|
|
4
5
不要使用挥发性物质。它实际上与线程安全无关。见 here 低下身。 对bool的分配不需要任何同步原语。如果你不做任何特别的努力,它会很好地工作。 如果要设置变量,然后确保另一个线程看到新值,则需要在两个线程之间建立某种通信。在分配前立即锁定什么也没有,因为在获得锁之前,另一个线程可能已经来了又走了。 最后一句警告:线程是非常难得到正确的。最有经验的程序员往往对线程的使用最不满意,这应该会为那些不熟悉线程使用的人敲响警钟。我强烈建议您在应用程序中使用一些更高级的原语来实现并发性。通过同步队列传递不可变的数据结构是一种大大减少危险的方法。 |
|
|
5
3
易变并不意味着记忆障碍。 它只意味着它将成为记忆模型感知状态的一部分。这意味着编译器不能将变量优化掉,也不能只在CPU寄存器中对变量执行操作(它将实际加载并存储到内存中)。 由于没有隐含的内存屏障,编译器可以随意重新排序指令。唯一的保证是不同的可变变量的读/写顺序将与代码中的顺序相同:
上面的代码(假设
|
|
|
6
2
问题3:关键部分和互斥体的工作方式基本相同。win32 mutex是一个内核对象,因此它可以在进程之间共享,并与waitformultipleobjects一起等待,而这是关键的\部分无法做到的。另一方面,关键部分的重量更轻,因此速度更快。但是代码的逻辑不应该受到您所使用的代码的影响。 您还评论说,“对于您想要保护的每个变量,不需要一个关键部分,如果您在关键部分,那么没有其他任何东西可以打断您。”这是正确的,但是权衡是,访问任何变量都需要您持有该锁。如果变量可以有意义地独立更新,那么您就失去了并行这些操作的机会。(不过,由于这些是同一对象的成员,所以在得出结论之前,我会仔细考虑,它们确实可以相互独立地访问。) |
|
|
Bogey · C#共享内存-CPU缓存风险(非易失性读取)? 8 年前 |
|
|
filo · 如何在x86上使用gcc强制执行内存排序 8 年前 |
|
|
maxim1 · Java中的易失阵列、内存障碍和可见性 9 年前 |
|
paddy · 双原子自旋锁的最小限制存储器排序 10 年前 |