【问题标题】:Is read/write of a bool value guaranteed to be one instruction in C/C++读/写 bool 值是否保证是 C/C++ 中的一条指令
【发布时间】:2012-03-24 01:33:54
【问题描述】:

我无法想象一个架构会设计一种在多条指令中对其最小数据类型的访问,但也许流水线存在一些我没有考虑的问题?

【问题讨论】:

  • C 和 C++ 都没有“指令”的概念,因此他们无法对此做出任何保证。另外,“一条指令”和“多个时钟周期”并不相互排斥。
  • 我不知道你在想什么,但是“一条指令”与“一个时钟周期”是完全不同的。对于大多数 CPU,大多数指令都需要多个周期。
  • 你绝对需要一个互斥体,不管bool 访问是否是一条指令。在其他潜在的惊喜中,如果您不使用锁,则不能保证不同的线程以相同的顺序看到来自其他线程的写入。数据竞争是未定义的行为:一旦代码被优化器破坏,各种错误都可能蔓延。
  • 如果你发现写入 bool 需要超过一个指令/时钟周期,你会怎么做?
  • @SteveJessop 对于大多数现代通用机器,每个时钟周期有不止一条指令。过去,RISC 机器每个时钟周期有一条指令,而 CISC 机器需要几个时钟周期来处理一条指令——在除法的情况下,有时需要数百个时钟周期。在现代机器上,流水线和多个并行执行单元意味着每个时钟通常可以执行多个指令。或者更准确地说,这个概念甚至没有真正的意义。

标签: c++ c types


【解决方案1】:

不,不能保证。

C89 和 C99 无法表达原子性。 C11 有原子对象。

编译器通常提供具有原子性的扩展:例如对于gcc

http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html

最好使用pthreads 库的一些原语。

【讨论】:

    【解决方案2】:

    C++ 标准不保证是否在单个操作中读取和写入 bool 对象,因为这会对底层硬件施加限制,从而C 和 C++ 尽量减少。

    但是,请注意,在多线程场景中,读/写数据类型是否是原子的问题只是问题的一半。另一半是对某个地址的更改是否反映在所有缓存中(即那些本地到不同内核的缓存),以及它们是否以相同的顺序反映在所有线程中。为此,您将需要内存屏障。

    【讨论】:

    • 到目前为止,所有答案都很棒。 sbi 我认为你得到了点头,因为你还提出了我根本没想到的多核。
    • 我很确定 C++03 不能保证 sizeof(bool) == sizeof(char),C++11 是吗?
    • @Steve No. sizeof(bool) 可以是任何东西。
    【解决方案3】:

    例如,您有 2 个线程使用相同的数据。

    您的线程 1 必须如下所示。让名称为“i”:

    while (true) {
               flag[i] = TRUE;
               turn = j;
               while ( flag[j] && turn == j);
    
                     CRITICAL SECTION
    
               flag[i] = FALSE;
    
                       REMAINDER SECTION
    
       }
    

    您的线程 2 必须如下所示。让我们将其命名为“j”:

    while (true) {
               flag[j] = TRUE;
               turn = i;
               while ( flag[i] && turn == i);
    
                     CRITICAL SECTION
    
               flag[i] = FALSE;
    
                       REMAINDER SECTION
    
       }
    

    标志变量控制每个线程进入临界区的入口。

    代码运行如下:

    1- 每个线程都想通过将其标志设置为 true 来进入临界区。

    2- 例如,线程“i”通过设置 turn 将其传递给线程“j”。转变量存储进入临界区的线程。

    3- 因为 turn 变量只能存储一个值。保证一个线程一次可以进入临界区。如果存在临界区,则没有其他线程可以进入临界区。

    4- 线程 j 看到标志点,pass 是它自己的并且想要进入。因此它可以进入临界区。而线程 i 等待。

    4- 在线程 j 运行之后。它通过确定自己不想进入临界区来将其标志变量设置为 false。

    5- 线程 i 在其 while 循环开始时被阻止。

    6- 只要线程 j 通过转动它的开头来轮到另一个线程。线程 i 进入临界区。

    此代码满足。互斥量、进度和边界等待条件。

    此代码可以运行所有支持线程的环境,并且可以与任何基于 C 的语言一起使用。

    【讨论】:

      猜你喜欢
      • 2010-09-08
      • 2011-01-16
      • 2018-11-05
      • 2011-05-19
      • 2015-02-11
      • 2011-06-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多