【发布时间】:2022-01-02 09:07:49
【问题描述】:
我有下面的 sn-p 代码,我想用它来展示 C 多线程编程中数据竞争的情况。我在 x86 Linux 系统(只有一个内核)上使用gcc test.c -O0 -lpthread -o test && ./test 编译并运行了近一百次,但我没有看到任何打印值不正确(200000)的情况。这是否意味着 x86 或编译器可以保证对 int 变量的每次修改都是线程安全的?还是我的程序有什么问题?
编辑:所以,正如我和@Sinic 测试的那样,问题将更新为: 为什么在单核 CPU 上很少发生数据竞速?或者在单核 CPU 上不会出现任何数据竞速问题? 因为,AFAIK,即使在单核 CPU 上线程也会随机调度。所以,结果也是一团糟。
// test.c
#include <threads.h>
#include <stdio.h>
#define THREAD_COUNT 20
#define THREAD_LOOP 10000
int counter = 0;
int run(void* data) {
for (int i = 0; i < THREAD_LOOP; i++)
counter++; // <- each thread would modify the global variable counter here.
printf("Thread %d terminates.\n", *((int*) data));
return thrd_success;
}
int main(void) {
#ifndef __STDC_NO_THREADS__
int ids[THREAD_COUNT];
thrd_t threads[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
ids[i] = i + 1;
thrd_create(&threads[i], run, ids + i);
}
for (int i = 0; i < THREAD_COUNT; i++)
thrd_join(threads[i], NULL);
printf("Counter value is: %d.\n", counter);
#endif
return 0;
}
得到了run函数的汇编代码截图如下图,还指出了counter++的对应代码。
【问题讨论】:
-
即使禁用了优化,您确定编译器根本不会在
run函数中展开循环吗?你看过生成的汇编代码吗?您是否尝试过使counter变量volatile? -
@Someprogrammerdude 我得到了run函数的汇编实现截图。
-
我在 Ryzen 3900X(12 核 + SMT)上循环运行了 10000 次代码:计数器从未准确达到 200000。它的范围从 13770 到 107813,平均值为 13770 到 107813。 30774 个。
-
@Sinic 这个问题可能与我在单核 CPU 上运行它有关吗?
-
@Becavalier 绝对。如果我只将过程固定到一个核心,则(在 10000 次测试中)有 ~9548 个正确结果。所以超过95%。我认为这是因为在单核机器上发生计数器更新时,与在多核机器上相比,它不太可能被准确安排。两个核心的正确结果下降到 47%,三个核心的正确结果下降到 9%
标签: c multithreading