2

在多线程或 RTOS 环境中,以下这些代码是否相同?

我相信他们不是。但是第一个代码绝对保存在多线程环境中吗?编译器是否有规则为“ga”分配一个寄存器,并且稍后在 func_a() 中不会再次读取“ga”?

我知道我可以使用锁,但这不是关于如何保护数据的问题。这只是关于编译器行为的问题。

// ga 是一个全局变量。

int func_a() {

    int a = ga;
    return a>2 ? a-2 : 2-a;
}

int func_b() {

    return ga>2 ? ga-2 : 2-ga;
}

我的意图是寻找一种标准方式(不是特定于平台的)来读取 ga 一次并将其值分配给局部变量“a”。

然后可以始终使用“a”,而不管“ga”是否已更改。

4

4 回答 4

2

C 标准中没有规则要求编译器以不同的方式实现这些函数。例如,当使用寄存器时,编译器可能会或可能不会“优化”从gato的分配a(即“优化”,我的意思是:加载ga到 REG,然后使用相同的 REG 进行其余的计算,使用它作为a)。或者它可能不会这样做。

如果要实现无锁数据结构:

  1. C99 没有提供任何可以帮助您的东西。
  2. C11(最新标准)为您提供原子数据类型。

如果您使用的是 C99,那么您需要:

  1. 使用锁(因此,不是无锁代码)
  2. 准备好编写特定于架构的代码。您至少需要使用一组最小的原子操作,就像在这个库中所做的那样,它使用 x86、x86_64 和 ARM ISA 提供的原子操作来实现无锁数据结构。

在此答案的早期版本中,我谈到了一个附带问题(与相关,并且与您的实际volatile问题实际上无关):

有一种情况可以限制如何func_b实现,但我实际上是在这里切线:如果ga被声明为volatile.

如果ga是易失性的,那么每次读取都ga 必须ga重新从内存中加载。即 in func_bga将从内存中加载两次。一次用于比较,一次用于计算返回值。例如,预期用途是ga指内存映射的 I/O 端口。然后,如果ga两次读取之间的值发生变化,这将反映在返回值中。但是,如果您ga在另一个线程中进行更改,请不要期望理智/定义的行为。

另一方面,没有限定符volatile并不意味着ga将在func_b. 并且没有与“易失性相反”的限定词。

于 2012-04-25T10:21:35.117 回答
2

面对执行函数的多个线程,这两个版本的代码都具有未定义的行为。当然,不同的编译器可以对是否将全局变量保存到寄存器中做不同的事情。更重要的是,对于正在改变全局变量的线程,不能保证分配给局部变量可以以原子方式完成。

于 2012-04-25T09:57:08.743 回答
0

行为取决于您使用的编译器,每个编译器都有自己的优化规则。

于 2012-04-25T10:01:40.137 回答
0

这两个片段很可能会以相同的机器代码结束。在多线程情况下,它们都不安全。

volatile会强制创建一个临时变量,但由于不能保证从“ga”复制到 volatile 变量是原子的,所以这不是线程安全的。

编写此类代码的唯一安全方法是使用警卫:

int func_a() {

    mtx_lock(&ga_mutex);
    int a = ga;
    mtx_unlock(&ga_mutex);

    return a>2 ? a-2 : 2-a;
}
于 2012-04-25T10:55:59.247 回答