如何实现一个可移植的指针比较和交换?

8

我在StackOverflow的一个回答中找到了这段关于compareAndSwap的代码:

boolean CompareAndSwapPointer(volatile * void * ptr,
                              void * new_value,
                              void * old_value) {
#if defined(_MSC_VER)
   if (InterlockedCompareExchange(ptr, new_value, old_value) == old_value) return false;
   else return true;
#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100
   return __sync_bool_compare_and_swap(ptr, old_value, new_value);
#else
#  error No implementation
#endif
}

这是除了内联汇编外,最合适的具有便携式快速代码的方法吗?

此外,一个问题在于不同编译器中那些特定的builtin方法具有不同的参数和返回值,这可能需要进行一些额外的更改,例如在此示例中使用if then else

另一个问题是这些builtin方法在机器代码层面上的行为,它们是否完全相同?(例如,是否使用相同的汇编指令)

注意:如果有许多支持的平台而不仅仅是(WindowsLinux)如本例所示,则另一个问题将是代码可能会变得非常庞大。


1
这是拥有可移植快速代码的最适当方式吗?(除了内联汇编)。内联汇编将极其不具备可移植性。 - Michael
1
std::atomic<T*> :-) - Kerrek SB
我目前正在使用C。 - Bionix1441
@Michael 你是正确的。 - Bionix1441
@Michael,所以为了让我和所有人都明白,汇编内联是依赖于微处理器体系结构还是编译器的?谢谢。 - Bionix1441
3个回答

3
我会使用硬件抽象层(HAL),允许通用代码成为共同的,任何可移植源都可以包含并构建到每个平台上。在我看来,这可以使源代码更好地结构化和更易于阅读。为了让您更好地理解这个过程,我建议您搜索Google以查找示例和说明。希望这个简短的答案有所帮助。
[编辑] 我将尝试为Bionix提供一个简单的示例,以展示如何实现HAL系统...
  • A先生想要他的应用程序在他的“天河2号”和他的“Amiga 500”上运行。他有交叉编译器等,并将在他的PC上构建两个二进制文件。他想要读取键并打印到屏幕上。
mrAMainApplication.c 包含以下内容...
#include "hal.h"

// This gets called every time around the main loop ...
void mainProcessLoop( void )
{
   unsigned char key = 0;

   // scan key ...
   key = hal_ReadKey();

   if ( key != 0 )
   {
       hal_PrintChar( key );
   }
}

他随后创建了一个头文件(请记住 - 这是一个例子,不是工作代码!)... 他创建了 hal.h ...
#ifndef _HAL_H_
#define _HAL_H_

unsigned char hal_ReadKey( void );
unsigned char hal_PrintChar( unsigned char pKey );

#endif // _HAL_H_

现在A先生需要两个分别用于“天河-2”系统和Amiga 500的源文件...
hal_A500.c
void hal_ReadKey( void )
{
    // Amiga related code for reading KEYBOARD
}

void hal_PrintChar( unsigned char pKey )
{
    // Amiga related code for printing to a shell...
}

hal_Tianhe2_VERYFAST.c

void hal_ReadKey( void )
{
    // Tianhe-2 related code for reading KEYBOARD
}

void hal_PrintChar( unsigned char pKey )
{
    // Tianhe-2 related code for printing to a shell...
}

当Mr A为Amiga构建应用程序时,他会构建mrAmainApplication.c和hal_A500.c。而在为天河二号构建时,他会使用hal_Tianhe2_VERYFAST.c代替hal_A500.c。

好的,我加入了一些幽默元素来编写这个例子,没有针对任何人,只是为了使例子更有趣,希望能帮助理解。

尼尔


我在谷歌上搜索过,但是我找到的都是关于它如何工作的一般性信息。您能否给我一个使用“HAL”概念的小例子的链接,它是使用“C”还是“C++”实现的? - Bionix1441
1
很抱歉Bionox1441,但我正在工作 - 我们的互联网访问受到限制。我注意到(http://stackoverflow.com/questions/12700909/simple-example-to-illustrate-writing-an-abstraction-layer-in-c)这至少可以让您更好地理解原则。如果这没有帮助,我今晚会寻找更好的例子。 - Neil
如果您有时间,能否给我一个例子? - Bionix1441
1
这里有一个例子 - (https://community.particle.io/t/what-is-hardware-abstraction-layer-and-and-how-to-implement-it/10590)。虽然不是很好,但它可以避免我尝试解释它。如果仍然不清楚,那么我会为您编辑我的答案,谢谢 - 尼尔 - Neil
1
我已经为您编辑了答案,Bionix - 它非常简单和轻松 - 但应该解释了平台相关源代码如何与通用代码一起使用。谢谢... - Neil

1
请看一下ConcurrencyKit,可能您可以使用更高级的原语,这可能是大多数人真正想要的。与某些特定于操作系统的HAL相比,我认为CK适用于Windows和许多非GCC编译器。
但是,如果您只对如何在各种C编译器上可移植地实现“比较和交换”或原子操作感兴趣,请查看并了解代码的工作方式。这都是开源的。
我怀疑细节可能会变得混乱,而且通常不会成为普通公众易于理解或有趣的阐述内容。

ConcurrencyKit非常针对这些并发情况,但在被用户编辑之前,我的问题是普遍的,不仅仅适用于比较和交换。我只是将其作为澄清问题的例子,以解释我目前遇到的问题。 - Bionix1441
1
请查看修改后的答案。 - rocky

1
在现代C语言中,从C11开始,使用_Atomic作为类型限定词,atomic_compare_exchange_weak作为函数。较新版本的gcc和clang符合C11标准,并以可移植的方式实现这些操作。

微软不使用现代C语言 - 这并没有帮助。 - Neil
@Neil,我认为它确实可以。它将需要编写异常的奇异平台的情况减少到1个,这就是标准的理念。此外,<stdatomic.h>有MS的实现,整个接口最初是为Windows上的一个库而发明的。 - Jens Gustedt
@Jens,这些https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html难道不比普通的更快吗?我认为这些内置方法在性能方面类似于宏扩展。 - Bionix1441
@Bionix1441,你所说的“正常的”是什么意思?就像我所说的,atomic_compare_exchange*接口是宏,解析为类似于这些内置函数的东西。作为这些东西的用户,这不应该是你关心的问题。只要想一想,编译器实现者会尽其所能使这些操作高效。由于gcc人员知道如何处理__buitlin,你可以确信他们也知道如何实现标准接口。 - Jens Gustedt
@Bionix1441,你好,似乎你对效率有一个错误的想法。实际上,时间上的真正限制因素是总线传输,可能最多需要几百个时钟周期。因此,原子操作从绝对意义上来说永远不是“高效”的,只是相对于使用互斥锁锁定临界区更加高效而已。 - Jens Gustedt
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接