【问题标题】:Atomic integer increment原子整数增量
【发布时间】:2013-12-11 17:48:12
【问题描述】:

让我们有一个递增全局原子整数的函数,然后我们将该值存储在非原子局部变量中

_Atomic int atom = 0 ;

void function( void )
{   
    int a = atom++ ;
}

将上述代码用于多线程时,a 是否始终具有唯一值。如果它会,正如一些评论者所建议的那样,那么像atomic_fetch_add 这样的函数有什么意义。

【问题讨论】:

  • 是什么让您认为对atomic_global_integer 的访问实际上是原子的?如果是这样,a 将不会在两个线程中收到相同的值。
  • @interjay 它是原子的。例如定义为_Atomic int;a 在第一种情况下肯定可以收到相同的值。
  • 在这种情况下,要么你的实现有问题,要么你在别处更改atomic_global_integer
  • 我支持我对您未显示的代码或实现中的错误的评论。如果您不相信,请发布complete example
  • @self.:显示代码,包括atomic_global_integer的定义和初始化。另外,您是如何得出不同线程可以获得相同值的结论的?

标签: c multithreading concurrency increment


【解决方案1】:

我不熟悉“_Atomic”关键字。这是新的 C++11 吗?在任何情况下,您都应该使用内置于大多数平台和 cpu 架构中的原子内在函数。 (在 x86 中又称为“锁定添加”)。

在 GCC/Clang 上,要调用的函数是 __sync_add_and_fetch。在 Windows 上,它被称为InterlockedIncrement

在一些仍然以 i386 为目标的 gcc 架构上,您必须在汇编中手动执行此操作。 (虽然这不适用于真正的 80386,但直到 80486 才引入锁定内在函数,但我离题了......)

unsigned int xadd_4(volatile void* pVal, unsigned int inc)
{
    unsigned int result;
    unsigned int* pValInt = (unsigned int*)pVal;

    asm volatile( 
        "lock; xaddl %%eax, %2;"
        :"=a" (result) 
        : "a" (inc), "m" (*pValInt) 
        :"memory" );

    return (result);
}

int AtomicIncrement(int* pInt)
{
    COMPILE_TIME_ASSERT(sizeof(int)==4);   
    // InterlockedIncrement
    unsigned int result = xadd_4(pInt, 1) + 1;
    return (int)result;
}

【讨论】:

  • 谢谢,这就是我要找的信息。 (有时对于实际信息,SO 可能毫无用处。)
  • 我假设 mutex 也可以工作。(甚至使用非原子)?
  • _Atomic 是 C11 中的新关键字,几乎不支持。
  • 互斥锁也可以。对于缺乏线程和原子经验的程序员来说更安全(对于有经验的程序员来说可能更安全)。
【解决方案2】:

您可以通过 CompareAndSwap 操作 (cas) 使用程序集插入。 对于 intel/gcc,以下示例工作(使用 cmpxchg):

static inline uint32_t u32cas(volatile uint32_t *ptr, uint32_t old_val, uint32_t new_val) {
   uint32_t prev_val;
   __asm__ __volatile__("lock; cmpxchgl %k1,%2"
                        : "=a"(prev_val)
                        : "q"(new_val), "m"(*(volatile uint32_t *)(ptr)), "0"(old_val)
                        : "memory");
   return prev_val;
}


uint32_t atom = 0;


void atomic_increment() {
  uint32_t x;
  do {
    x = atom;
  } while(u32cas(&atom, x, x + 1) != x);
}

另外,在 FreeBSD 上,您可以使用

人原子

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    相关资源
    最近更新 更多