以前我已经写了一些非常简单的多线程代码,我一直意识到,在任何时候都有可能是一个背景下开关右在中间的是什么我在做,所以我总是把守的访问共同变量通过一个CCriticalSection类进入关键部分在建设和叶子,它破坏。我知道这是相当积极的,我进入和离开关键的部分相当频繁地和异乎寻常,有时(例如在开始的一个函数时候我可以把CCriticalSection内部更紧密码的方块),但我的代码不会崩溃和它的运行速度不够快。

在工作我多线程代码需要一个更严格,只有锁定/同步在最低水平的需要。

在工作我只是想试一些多线程代码,我遇到了这样的:

EnterCriticalSection(&m_Crit4);
m_bSomeVariable = true;
LeaveCriticalSection(&m_Crit4);

现在, m_bSomeVariable 是Win32BOOL(不易挥发),因为据我所知被定义为是一个int,并在x86阅读和写作的这些值是一个单一的指令,并由于背景下开关发生在一个指示边界后就没有必要同步这一作用的一个关键部分。

我做了一些更多的研究网上看是否本操作并不需要同步,和我想出了两种情况下,它所做的:

  1. CPU实现出的订单执行或第二线上运行的一个不同的核心和更新的价值是不是写入RAM的其他核心要见;和
  2. Int不是4-byte对准。

我相信号1是可以解决使用的"挥发性"的关键词。在VS2005和后来C++编译器访问围绕着以这个变量使用的存储器的障碍,确保可变总是完全写入/读到的主要的系统存储器之前使用。

2号,我无法验证,我不知道为什么字节对准会使有差别。我不知道x86指令设定,但不会 mov 需要给予4个字节结盟的地址吗?如果不这样做,你需要使用一个组合的指令?这将介绍该问题。

所以...

问题1: 不会使用的"挥发性"的关键词(implicity使用记忆障碍和暗示来编译器不以优化这个代码)免除一个程序员,需要同步的4个字节/8-byte在x86/64变量之间的读写操作?

问题2: 是有明确要求这一变量将4-字节/8-byte结盟?

我做了一些更深入挖掘我们的代码和变量的定义类:

class CExample
{

private:

    CRITICAL_SECTION m_Crit1; // Protects variable a
    CRITICAL_SECTION m_Crit2; // Protects variable b
    CRITICAL_SECTION m_Crit3; // Protects variable c
    CRITICAL_SECTION m_Crit4; // Protects variable d

    // ...

};

现在,对我来说,这似乎过多。我认为关键的部分同步的线之间一个过程,所以如果你有一个你可以进入,它并没有其他线在这一过程中可以执行。没有必要为一个关键部分,为每个变量的想要保护,如果你在一个关键部分,然后就没有什么可以打断你。

我想唯一的可能变化的变量从外一个关键部分是,如果过程中共享存储器的一页,与另一个进程(你可以做的吗?) 与其他进程开始改变价值观。互斥也将有助于在这里,名为互斥共享程序,或仅处理相同的名字吗?

问题3: 是我的分析的关键部分正确,应这些代码被改写使用互斥?我有看一看其他同步的对象(信号灯和自旋锁),它们更适合在这里?

问题4: 其中关键部分/互斥/信号灯/自旋锁最适合?这就是,它同步的问题,他们应该被应用到。是有一个巨大的性能损选择一个比其他?

虽然我们在这,我读了自旋锁,不应该用在一个单一的核心多线程的环境,只有一个多芯多线程的环境。所以, 问题5: 这是错误的,或者如果没有,为什么它的权利?

预先感谢任何答复:)

有帮助吗?

解决方案

问题1:使用的"挥发性"的关键词

在VS2005和后来C++编译器访问围绕着以这个变量使用的存储器的障碍,确保可变总是完全写入/读到的主要的系统存储器之前使用。

准确。如果你不是创造便携式码,Visual Studio实现它正是这样。如果你想来是便携式的,你的选择是目前"有限"。直到C++0x有没有便携式方式如何指定的原子操作与保证阅读写的订购和需要实现每个平台的解决方案。这就是说,提高已经做过的肮脏的工作,并且可以使用 它原子的原语.

问题2:变量需要4个字节/8-byte结盟?

如果你保持它们对准时,你是安全的。如果你不这样做,规则复杂化(缓线,...),因此最安全的办法是让他们结盟,因为这是容易实现的。

问题3:应这些代码被改写使用互斥?

关键的部分是轻量级的互斥。除非你需要同步进程之间,可使用的关键部分。

问题4:其中关键部分/互斥/信号灯/自旋锁最适合?

关键的部分 甚至可以 做旋等待 对于你。

问题5:自旋锁,不应该用在一个单一的核心

旋锁使用的事实,同时等待CPU是纺纱,另一个CPU可能释放锁。这不可能发生的一个CPU只,因此它只是一个浪费时间。在多CPU旋锁可以是很好的想法,但它取决于你如何经常旋等待将是成功的。这个想法是在等待一个短暂的,而是很快,然后做下开关那里,然后再返回,因此,如果等待它可能短,最好是等待。

其他提示

1)没有挥发性只是说重新加载的价值从存储器中的每一时间,它仍然是可能的,对于它是半更新。

编辑:2)Windows提供了一些原子的功能。看看 "联锁"的功能.

评论意见使我做了一些更多的阅读。如果你阅读过 英特尔的系统编程指南 你可以看到,有齐阅读和写的都是原子.

8.1.1保证原子操作 该Intel486处理器(和较新的理由)可以保证下面 基本存储器操作,将一直进行自动:
•阅读或写字节
•阅读或写字盟在16位的边界
•阅读或写双字上对准一个32位的边界
奔腾处理器(和较新的理由)可以保证下面 额外存储器操作,将一直进行自动:
•阅读或写入一个四字盟在64位的边界
•16位访问以非缓存存储的地点适合的一个32位数据的巴士
P6家庭处理器(和新的处理,因为)保证以下 额外存储器操作将一直进行自动:
•不对齐的16,32和64位访问缓存存储器内一个高速缓存 线
访问缓存,分割巴的宽度,缓线, 页边界不能保证原子通过英特尔的核心2Duo,英特尔 原子,英特尔的核心,奔腾M、奔腾4,英特尔强,P6家庭,奔腾, Intel486处理器。英特尔的核心2Duo,英特尔原子,英特尔的核心,奔腾M, 奔腾4,英特尔强和P6家庭处理器提供旅控制信号 允许外部存储器的子系统,使分裂的访问原子;但是, 不结盟数据访问会严重影响处理器的性能和 应该避免。一个x87指令或证的指示,访问的数据更大的比一个四字 可以实现使用多个内存访问。如果这样的指令商店 存储器,一些访问可以完成(文字来存储器),而另一个 导致操作错建筑的原因(例如由于一页表项目 标记"不存在").在这种情况下,影响完成的访问 可以看到的软件,虽然总体指令而引起的故障。如果TLB 无效已经推迟(见第4.10.3.4),诸页错误可能发生 即使所有访问都是为同一页上。

所以基本上是的如果你做一个8位读写任何一个地址16位读写从16位排列地址等等等等你得到原子操作。它还感兴趣地注意到,你可以做不对齐存储器的读写内cacheline在一个现代化的机器。该规则似乎相当复杂的,虽然这样我就不会依赖它们,如果我是你。欢呼的意见,这是一个良好的学习经验对我来说,一个:)

3)一个关键部分将试图旋锁定它锁了几次然后锁定一个互斥。旋锁定可以吸CPU力做什么和一个互斥可能需要一段时间才能做的东西。CriticalSections是一个好的选择,如果你不能使用互锁的功能。

4)有效惩罚的选择之一。它的一个很大的问去过的好处这里的一切。MSDN帮助有多好的信息在每个这些。我sugegst阅读。

5)可以使用旋锁在一个单一的螺纹环境通常不必要的,虽然线管理意味着你不能拥有2处理访问数据相同同时进行。它不仅仅是可能的。

1:易失性 在本身 实际上是无用的多线程。它保证,读写将被执行,而不是储存的价值的一个登记册,并向它保证,读写不会重新排序 相对于其他的 volatile 读写.但它仍可能重新排序,对于非挥发性的,它基本上是99.9%。Microsoft具有重新定义 volatile 也包装所有的访问在内存障碍,但是不能保证情况下。它将只是默默地打破对任何其定义编译器 volatile 作为标准。(码将编纂和运行,它就不应线安全的任何更长时间)

此外,读写到整数大小的物体是原子x86只要对象是对齐。(你没有保证 写会发生。编译器和CPU可以重新排序,因此它的原子,但不是线安全)

2:是的,对象必须结盟的读写能原子.

3:不是真的。只有一线可执行代码 里面给关键部分 在一段时间。其他的线仍然可以执行的其他代码。所以你可以有四个变量,每个受保护,由一个不同的关键部分。如果他们的所有共用相同的关键部分,我无法操纵的对象1时你在操纵目2,这是效率低下,限制了并行超过必要的。如果他们是保护不同的关键部分,我们不能这两种操纵的 目同时进行。

4:自旋锁是 很少 一个好主意。他们是有用的,如果你期待一个线程必须等待的只有非常短的时间才能够获得锁, 你绝对之前的评价会显示最低的延迟。它避免了OS背景下开关是一个相对较慢操作。相反,该线只是坐在一个循环不断地投票的一个变量。所以较高的占用(核心不是释放运行的另外一个线程,同时等待调节锁),但是线将能够继续 尽快 作锁被释放。

至于其他的性能特征是几乎相同的:只是使用者有义最适合你的需要。通常关键的部分是最方便的用于保护共同变量以及互斥可以很容易地用于设定一个"标记"允许其他线进行。

作为为不使用自旋锁在一个单一核心环境中,请记住,调节锁实际上不产率。线上等待调节锁实际上并没有留在允许的操作系统安排线B运行。但是,由于一个正在等待该调节锁,其他一些线是打算要释放锁。如果你只有一个单一的核心,那么,其他线将只能够运行时被交换。有一个理智的操作系统,就是将要发生或早或晚,无论如何作为经常方面的切换。但是,由于我们知道,一个不能获得锁,直到B有一个时间来执行和 释放 锁,我们将会更好,如果一个公正产生了立即的,是把排队等待通过的操作系统,并重新启动的时候B已释放锁。这就是所有 其他的 锁定类型做。调节锁仍然会 工作 在一个单一核心环境(假定一个系统,抢占多任务),它就能非常有效率非常低。

不使用挥发性的。它几乎没有什么要做到有线的安全。看看 在这里, 对于低下。

分配到BOOL不需要任何同步元。它会工作没有任何特别的努力的一部分。

如果你想要设置的变量,然后确保另一个线程中看到新的价值,需要建立某种两者之间的通信线。只是锁定之前立即将实现什么,因为其他线可能会来来去去之前获得锁。

最后一个忠告:线是非常难得到的权利。最有经验的程序员往往是少适用螺纹,其中应该设置警钟长鸣的人是没有经验他们的使用。我强烈建议您使用的一些更高级别的元,以实现并发在你的程序。通过不可改变的数据结构的通过同步队列是一个办法,大大降低了危险。

易失性并不意味着存的障碍。

它只意味着它将部分被察觉的状态存储器模型。其含义是,编译器无法优化可变务,也不能执行的操作上的变量只在CPU寄存器(它实际上将装载和储存以存储器)。

因为没有内存的障碍暗示,编译器可以重新排序的指示。唯一的保证是,为了在不同的挥发性变量读写将是相同的代码:

void test() 
{
    volatile int a;
    volatile int b;
    int c;

    c = 1;
    a = 5;
    b = 3;
}

与上述代码(假设 c 不是最优化的程)的更新 c 可能发生之前或之后的更新 ab, 提供3个可能的结果。的 ab 更新保证执行为了。 c 可以优化远很容易地通过任何编译器。有足够的信息,编译器,甚至可以优化远 ab (如果它能证明没有其它线阅读的变量,它们是不必要的硬件阵列(因此,在这种情况下,他们可以在实际上被移除)。注意,标准不需要一个特定的行为,而是一种可感知的状态的 as-if 规则。

问题3:CRITICAL_SECTIONs和互斥的工作,很多的,相同的方式。Win32互斥是一个核心目的,因此它可以共享之间的进程,并等待着关于与WaitForMultipleObjects,这你不能做一个CRITICAL_SECTION.另一方面,CRITICAL_SECTION是的轻重和因此得更快。但逻辑的代码应该不会影响其使用。

你还评论说,"没有必要为一个关键部分,为每个变量的想要保护,如果你在一个关键部分,然后就没有什么可以打断你。" 这是真实的,但权衡的是,访问的任何变量会需要你来拿锁。如果变量可有意义地进行更新独立,你正在失去一个机会,parallelising这些行动。(因为这些都是成员相同的对象,虽然,我会觉得很难在结束发言之前,他们真的可以被访问彼此独立。)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top