Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
编译器相关-这两个C代码真的相同吗?_C_Multithreading_Compiler Construction_Rtos - Fatal编程技术网

编译器相关-这两个C代码真的相同吗?

编译器相关-这两个C代码真的相同吗?,c,multithreading,compiler-construction,rtos,C,Multithreading,Compiler Construction,Rtos,在多线程或RTOS环境中,以下代码是否相同 我相信他们不是。但在多线程环境中,第一个代码是绝对保存的吗?编译器是否有规则为'ga'分配寄存器,并且以后不会在func_a中再次读取'ga' 我知道我可以使用锁,但这不是关于如何保护数据的问题。这只是一个关于编译器行为的问题 //遗传算法是一个全局变量 int func_a() { int a = ga; return a>2 ? a-2 : 2-a; } int func_b() { return ga>2

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

我相信他们不是。但在多线程环境中,第一个代码是绝对保存的吗?编译器是否有规则为'ga'分配寄存器,并且以后不会在func_a中再次读取'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”


无论“ga”是否已更改,“a”都可以始终使用。

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

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

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

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

在C标准中没有任何规则要求编译器以不同的方式实现这些函数。e、 g.当使用寄存器时,编译器可能会或可能不会“优化”从ga到a的赋值,即“优化”,我的意思是:将ga加载到一个寄存器中,然后使用相同的寄存器来完成剩余的计算,将其用作a。或者它可能不会这样做

int func_a() {

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

int func_b() {

    return ga>2 ? ga-2 : 2-ga;
}
如果要实现无锁数据结构,请执行以下操作:

C99没有提供任何帮助。 最近的标准为您提供了原子数据类型。 如果您使用的是C99,则您需要:

使用锁,而不是无锁代码 准备好编写特定于体系结构的代码。您至少需要使用一组最小的原子操作,如中所做的那样,使用x86、x86_64和ARM ISA提供的原子操作实现无锁数据结构。 在这个答案的早期版本中,我提到了一个与volatile有关的次要问题,这与您真正的问题并不相关:

有一种情况可以限制func_b的实现方式,但实际上我在这里偏离了正切:如果ga被声明为volatile

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


另一方面,没有volatile限定符并不意味着ga在func_b中只读取一次。而且没有“volatile的反面”的限定符。

C标准中没有要求编译器以不同方式实现这些函数的规则。e、 g.当使用寄存器时,编译器可能会或可能不会“优化”从ga到a的赋值,即“优化”,我的意思是:将ga加载到一个寄存器中,然后使用相同的寄存器来完成剩余的计算,将其用作a。或者它可能不会这样做

如果要实现无锁数据结构,请执行以下操作:

C99没有提供任何帮助。 最近的标准为您提供了原子数据类型。 如果您使用的是C99,则您需要:

使用锁,而不是无锁代码 准备好编写特定于体系结构的代码。您至少需要使用一组最小的原子操作,如中所做的那样,使用x86、x86_64和ARM ISA提供的原子操作实现无锁数据结构。 在这个答案的早期版本中,我提到了一个与volatile有关的次要问题,这与您真正的问题并不相关:

有一种情况可以限制func_b的实现方式,但实际上我在这里偏离了正切:如果ga被声明为volatile

若ga是易失性的,那个么ga上的每个读取都必须从内存重新加载ga。i、 e.在func_b中,ga将从内存中加载两次。一次用于比较,一次用于计算返回值。例如,预期用途是ga指内存映射的I/O端口。那么如果瓦卢 两次读取之间ga的e变化,这将反映在返回值中。但是,如果在另一个线程中更改ga,不要期望正常/定义的行为


另一方面,没有volatile限定符并不意味着ga在func_b中只读取一次。而且没有与volatile相反的限定符。

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

volatile将强制创建一个临时变量,但由于从ga到volatile变量的拷贝不能保证是原子的,因此这不是线程安全的

编写此类代码的唯一安全方法是使用防护装置:

int func_a() {

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

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

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

volatile将强制创建一个临时变量,但由于从ga到volatile变量的拷贝不能保证是原子的,因此这不是线程安全的

编写此类代码的唯一安全方法是使用防护装置:

int func_a() {

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

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


那么,获取全局变量的“快照”并在不关心其值是否已更新的情况下使用它的可靠方法是什么除了使用锁。使用锁是有效的。特定编译器对volatile的实现可能足以允许原子读写。但是很明显,如果没有锁,你仍然会有竞争。如果这个函数在一个参数中被传递了ga的地址,并且不得不取消引用它,会发生什么呢?这会改变什么吗?这就是锁的作用。当你有锁可以完美解决的精确问题时,为什么不想使用锁呢?如果您的特定平台提供了做同样事情的其他东西,那么您可以使用它。但是为什么不使用锁呢?我正在考虑一个无锁的循环缓冲区。当只有一个消费者和一个生产者时,应该不需要使用锁。但是,也有一些情况下,我需要读取循环缓冲区的head和tail指针,以使缓冲区状态为full,空等。这些读取需要获取指针的“快照”,以便结果一致。那么,获取全局变量的“快照”,然后在不关心其值是否已更新的情况下使用它的可靠方法是什么除了使用锁。使用锁是有效的。特定编译器对volatile的实现可能足以允许原子读写。但是很明显,如果没有锁,你仍然会有竞争。如果这个函数在一个参数中被传递了ga的地址,并且不得不取消引用它,会发生什么呢?这会改变什么吗?这就是锁的作用。当你有锁可以完美解决的精确问题时,为什么不想使用锁呢?如果您的特定平台提供了做同样事情的其他东西,那么您可以使用它。但是为什么不使用锁呢?我正在考虑一个无锁的循环缓冲区。当只有一个消费者和一个生产者时,应该不需要使用锁。然而,也有一些情况下,我需要读取循环缓冲区的头和尾指针,以获得缓冲区状态满、空等。这些读取需要获取指针的“快照”,以便结果一致。嘿!好的观点,但让我们假设它是..是的,假设它是先发制人的,而且ISR可能会更改“ga”。您使用的是什么线程标准?这是一个Windows线程问题吗?POSIX线程问题?您只有特定的线程标准、编译器或平台提供的所有保证。既然你没有告诉我们这三件事中的任何一件,所有的赌注都没有了。根据C标准,这是完全未定义的行为,因为它从未提及线程。从您对答案的评论中,我推测您特别希望避免使用锁。为什么你想避免使用锁?那么答案是它无法做到。仅使用C标准,不可能在线程之间安全地交换数据。你需要一些超越C标准的东西,比如原子操作、内存屏障等等!好的观点,但让我们假设它是..是的,假设它是先发制人的,而且ISR可能会更改“ga”。您使用的是什么线程标准?这是一个Windows线程问题吗?POSIX线程问题?您只有特定的线程标准、编译器或平台提供的所有保证。既然你没有告诉我们这三件事中的任何一件,所有的赌注都没有了。根据C标准,这是完全未定义的行为,因为它从未提及线程。从您对答案的评论中,我推测您特别希望避免使用锁。为什么你想避免使用锁?那么答案是它无法做到。仅使用C标准,不可能在线程之间安全地交换数据。你需要一些超出C标准的东西,比如原子操作、内存屏障等等。我理解volatile的用法。我的问题更多的是相反的
“volatile”打算实现什么。我只想读取“ga”一次,然后使用它的值,而不想再次读取,以避免得到不一致的结果,即a>2?a-2:2-a@user184258:对于编译后的代码是一次读取ga还是多次读取ga,以及该读取是否为原子读取,绝对无法保证。对不起,这里的语言标准不能保证任何东西。这可能在某些CPU和某些编译器上按您的意愿工作,但不一定要这样,在其他CPU和其他编译器上则不行。@Alex+1。正当另外:我编辑了答案以澄清“没有volatile”不能保证一次读取。我理解volatile的用法。我的问题更多的是关于“volatile”想要实现的目标的反面。我只想读取“ga”一次,然后使用它的值,而不想再次读取,以避免得到不一致的结果,即a>2?a-2:2-a@user184258:对于编译后的代码是一次读取ga还是多次读取ga,以及该读取是否为原子读取,绝对无法保证。对不起,这里的语言标准不能保证任何东西。这可能在某些CPU和某些编译器上按您的意愿工作,但不一定要这样,在其他CPU和其他编译器上则不行。@Alex+1。正当另外:我对答案进行了编辑,以澄清“不具有volatile”并不保证一次读取。也可能存在特定于硬件的行为,而不仅仅是特定于编译器的行为。也可能存在特定于硬件的行为,而不仅仅是特定于编译器的行为。ga是否仍然需要具有volatile?编译器现在无法将读取移动到互斥操作之前/之后,该如何处理?@Alex No,ga不需要是易失性的。编译器可以从很多方面了解它,但这不是你的问题,而是设计互斥操作和构建编译器的人的问题。它必须知道,否则互斥操作将被破坏。通常的一种方法是在互斥调用中内置一个特殊的编译器,称为。在某些平台上,它之所以有效,是因为编译器不能假定互斥操作本身不修改ga。这是特定于平台的。@Alex编译器可能会更改代码的执行顺序,前提是它可以证明该顺序不会影响结果。它可以进行这样的优化,以提供更好的指令管道、分支预测等。在多线程、多核环境中,这样的优化可能是危险的,如果一个核做出假设并决定在执行代码之前缓存指令,那么另一个核会修改该值。为了防止这样的错误,必须使用的概念。继续->@Alex在某些情况下,编译器通过volatile关键字实现内存屏障,但这并不能保证。一些编译器/操作系统将内存屏障和互斥锁一起实现,我认为这是最常见的。它没有标准。ga是否仍然需要易变?编译器现在无法将读取移动到互斥操作之前/之后,该如何处理?@Alex No,ga不需要是易失性的。编译器可以从很多方面了解它,但这不是你的问题,而是设计互斥操作和构建编译器的人的问题。它必须知道,否则互斥操作将被破坏。通常的一种方法是在互斥调用中内置一个特殊的编译器,称为。在某些平台上,它之所以有效,是因为编译器不能假定互斥操作本身不修改ga。这是特定于平台的。@Alex编译器可能会更改代码的执行顺序,前提是它可以证明该顺序不会影响结果。它可以进行这样的优化,以提供更好的指令管道、分支预测等。在多线程、多核环境中,这样的优化可能是危险的,如果一个核做出假设并决定在执行代码之前缓存指令,那么另一个核会修改该值。为了防止这样的错误,必须使用的概念。继续->@Alex在某些情况下,编译器通过volatile关键字实现内存屏障,但这并不能保证。一些编译器/操作系统将内存屏障和互斥锁一起实现,我认为这是最常见的。它没有标准。