C++ 子字大小标志的线程安全性

C++ 子字大小标志的线程安全性,c++,multithreading,thread-safety,C++,Multithreading,Thread Safety,考虑以下场景: 存在一个全局可访问的变量F 线程A反复为F分配一个随机值(不考虑F的先前值) 线程B执行与线程A相同的操作(独立于A) 线程C重复读取F的值(并打印它) 这是在Windows上的C++(Visual C++)上的X64架构,多处理器。code>F属于bool类型,并标记为volatile,且所有访问均不受任何锁的保护 问题:这个场景是否有线程不安全的地方? 假设代码的逻辑行为是有效的,那么多个线程同时将值读写到同一位置这一事实是否有任何不安全之处 (跨体系结构、操作系统、编译器)

考虑以下场景:

  • 存在一个全局可访问的变量
    F
  • 线程
    A
    反复为
    F
    分配一个随机值(不考虑
    F
    的先前值)
  • 线程
    B
    执行与线程
    A
    相同的操作(独立于
    A
  • 线程
    C
    重复读取
    F
    的值(并打印它)
  • 这是在Windows上的C++(Visual C++)上的X64架构,多处理器。code>F属于

    bool
    类型,并标记为
    volatile
    ,且所有访问均不受任何锁的保护

    问题:这个场景是否有线程不安全的地方?

    假设代码的逻辑行为是有效的,那么多个线程同时将值读写到同一位置这一事实是否有任何不安全之处


    (跨体系结构、操作系统、编译器)如何保证从变量读取和写入变量的原子性取决于线程安全要求。您总是会得到一个一致的值(也就是说,不可能从线程a的write中得到一半的值,而从线程B中得到另一半的值),但是不能保证您将读取的值实际上是逻辑上写入的最新值


    这里的问题是CPU缓存可能会被刷新,也可能不会被刷新。当线程写入内存时,值首先进入缓存,最后写入内存。在此期间,如果其他内核尝试从内存中读取对象,它们将获得旧值。

    在x86下,对与其大小正确对齐的类型的任何读取或写入都被认为是原子的(因此对于
    bool
    它只需要是一个1字节的对齐边界),但是为了便于携带和提供内存障碍,建议使用explict atomic ops。摘自《英特尔系统编程指南》,第3A卷,第8节。2011年5月(还有一个,在atm机上找不到)

    Intel486处理器(以及此后更新的处理器)保证: 基本内存操作将始终以原子方式执行:

    •读取或写入一个字节

    •读取或写入16位边界上对齐的字

    •读取或写入在32位边界上对齐的双字

    奔腾处理器(以及此后的较新处理器)保证: 附加内存操作将始终以原子方式执行:

    •读取或写入64位边界上对齐的四字

    •对32位数据总线中未缓存内存位置的16位访问

    P6系列处理器(以及此后的较新处理器)保证以下各项 附加内存操作将始终以原子方式执行:

    •对适合缓存的缓存内存进行未对齐的16、32和64位访问 线

    Microsoft也有使用
    volatitle bool
    发出线程退出信号的方法,但是,如果您想发出正在等待的线程的信号,最好使用内核构造,在windows上这将是一个事件(请参见
    CreateEventA/W
    ),这将防止等待在变量尚未设置时燃烧cpu周期

    更新:

    对于几乎有零等待时间的线程,实现一个用户级锁是一个好主意,如果有一个高的竞争环境,可以选择退避,英特尔有一个很好的文章,或者C++可以使用WiAPI的关键部分(这些是半内核级)。为您提供读取/写入的获取/释放语义。这难道不能解决你在我的具体案例中描述的问题吗?但是,是的,我同意你的观点是正确的。我不确定MSDN声明是否适用于x64。此外,我也看到其他人在这个网站上争辩说,即使对于x86,这种说法也不完全正确。如果这个变量控制关键部分的并发性,我建议您应该使用

    CriticalSection
    Mutex
    (如果并发需要是进程间的)@TripShock,是的,我相信Microsoft对
    volatile
    的扩展解决了这个问题(虽然我自己不能支持这些说法,而Praetorian似乎持相反的观点)。如果MSVC++支持C++11的原子类型,我会支持它们,因为它们是为这种情况设计的。你是对的,在TripShock的简化场景中,没有保证他(从任何线程,当前或旧线程)获得什么价值但是,我不知道这个场景是否过于简单。因为从您开始读取从一个线程写入的多个变量的那一刻起,CPU确实有一些排序保证(通常数据是按照写入的顺序刷新的)。“最新”对于MT编程来说,没有“最新”这样的东西任何东西。正如我在问题的最后一部分中提到的,没有等待。如果有等待,是的,使用事件或其他内核对象将是一个好主意。@TripShock:更新无等待事件的问题,至于ARM,我没有任何线索,您需要查阅他们的体系结构手册来完成此任务在ion中,您可以假设没有关键资源或任何类似的东西需要保护。问题仅仅是在线程之间发送信号表示某些操作的完成状态,而不涉及任何等待。简单的示例是一个线程向另一个线程发送信号,表示现在可以停止正在执行的操作,然后退出t、 需要注意的是,
    bool
    通常被编译为本机字或int大小,因此可以是32位或64位。也就是说,它很有可能不是子字大小。所以,您只想取消一个线程?