4

最近在工作中,想在一个多线程程序中实现一个计数器。我发现我的 GCC(3.4.5) 中有一个名为atomic_t的用户空间数据类型。但它似乎不是真正的原子。

我在具有 12 个内核且 linux 内核为 2.6.9 的 x86_64 机器上 测试了 atomic_inc()/atomic_read() 。

这是演示。我添加了 pthread_cond_t 和 pthread_cond_broadcast 来增加并发度。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <asm/atomic.h>

atomic_t atomic_count;
pthread_cond_t g_cond;
pthread_mutex_t g_mutex;

void* thread_func(void*p) {
    pthread_mutex_t* lock = (pthread_mutex_t*)p;
    pthread_mutex_lock(lock);
    pthread_cond_wait(&g_cond, lock);
    pthread_mutex_unlock(lock);

    for (int i=0;i<20000;++i){
        atomic_inc(&atomic_count);
    }
    return NULL;
}
#define THRD_NUM 15

int main() {
    atomic_set(&atomic_count, 0);
    pthread_cond_init(&g_cond, NULL);
    pthread_mutex_init(&g_mutex, NULL);
    pthread_t pid[THRD_NUM];

    for (int i=0; i<THRD_NUM; i++) {
        pthread_create(&pid[i], NULL, thread_func, &g_mutex);
    }

    sleep(3);
    pthread_cond_broadcast(&g_cond);

    for (int i=0; i<THRD_NUM; i++) {
        pthread_join(pid[i], NULL);
    }

    long ans = atomic_read(&atomic_count);
    printf("atomic_count:%ld \n", ans);
}

预期结果是 300000,但我们总是得到 270000+ 或 280000+。我发现atomic_inc()的实现是

 static __inline__ void atomic_inc(atomic_t *v)
 {
     __asm__ __volatile__(
         LOCK "incl %0"
         :"=m" (v->counter)
         :"m" (v->counter));
 }

根据英特尔手册,LOCK前缀具有完全屏障的语义。这是否意味着程序的输出与指令重新排序无关?

更重要的是,我发现了一个有趣的现象。如果我将THRD_NUM设置为小于 12(我机器的核心数),则错误的频率会更低。我认为这可能是由上下文切换引起的。但我不知道这是怎么发生的。有人能帮我吗?谢谢!

4

3 回答 3

2

LOCK 是一个宏。您确定它实际上被定义为“锁定”,因为它应该是实际做任何事情吗?

我很确定您使用的标头仅适用于内核。“asm”下的东西不应该在用户空间中使用。

于 2013-07-25T15:10:44.847 回答
0

我假设<asm/atomic.h>是一些 Linux 内核头文件?它当然不是 C 的一部分。 在 C11 中,您可以通过<stdatomic.h>. 不过,您需要切换到更新的 gcc。

我不知道您LOCK从哪里获取宏,但我认为它的定义不正确。在Linux 的 asm/atomic.h 版本中,我通过 Google 搜索发现他们使用了一个名为 的宏LOCK_PREFIX,而不是LOCK.

这是他们的 atomic_inc 代码:

93 static inline void atomic_inc(atomic_t *v)
94 {
95         asm volatile(LOCK_PREFIX "incl %0"
96                      : "+m" (v->counter));
97 }

这应该变成:

asm volatile("lock incl %0"
             : "+m" (v->counter));

我将不再为宏而烦恼,只需将字符串放入("lock incl %0")中。

然后编译gcc -S myfile.c并确保在myfile.s你看到的lock incl指令,而不仅仅是一个简单的incl(没有lock)。

于 2013-07-25T17:00:05.497 回答
-3

我相信

atomic_t atomic_count;

应该

volatile atomic_t atomic_count;

试试看。

于 2013-07-25T14:55:39.607 回答