c语言多线程的作用是什么意思,多线程-如何在普通C语言中启动线程?

C11螺纹+ C11 cbnz

已添加到glibc 2.28。 通过从源代码编译glibc 2.28,在Ubuntu 18.10 amd64(glic 2.28附带)和Ubuntu 18.04(glibc 2.27附带)中进行了测试:在单个主机上有多个glibc库

改编自以下示例:[https://en.cppreference.com/w/c/language/atomic]

main.c

#include

#include

#include

atomic_int atomic_counter;

int non_atomic_counter;

int mythread(void* thr_data) {

(void)thr_data;

for(int n = 0; n < 1000; ++n) {

++non_atomic_counter;

++atomic_counter;

// for this example, relaxed memory order is sufficient, e.g.

// atomic_fetch_add_explicit(&atomic_counter, 1, memory_order_relaxed);

}

return 0;

}

int main(void) {

thrd_t thr[10];

for(int n = 0; n < 10; ++n)

thrd_create(&thr[n], mythread, NULL);

for(int n = 0; n < 10; ++n)

thrd_join(thr[n], NULL);

printf("atomic %d\n", atomic_counter);

printf("non-atomic %d\n", non_atomic_counter);

}

GitHub上游。

编译并运行:

gcc -ggdb3 -std=c11 -Wall -Wextra -pedantic -o main.out main.c -pthread

./main.out

可能的输出:

atomic 10000

non-atomic 4341

由于跨线程访问非原子变量,因此非原子计数器很可能小于原子计数器。

另请参阅:如何在C中进行原子增量和获取?

拆卸分析

拆卸:

gdb -batch -ex "disassemble/rs mythread" main.out

包含:

17 ++non_atomic_counter;

0x00000000004007e8 : 83 05 65 08 20 00 01 addl $0x1,0x200865(%rip) # 0x601054

18 __atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST);

0x00000000004007ef : f0 83 05 61 08 20 00 01 lock addl $0x1,0x200861(%rip) # 0x601058

因此,我们看到原子增量是在指令级使用cbnz锁前缀完成的。

使用cbnz 8.2.0,我们得到的是:

11 ++non_atomic_counter;

0x0000000000000a28 : 60 00 40 b9 ldr w0, [x3]

0x0000000000000a2c : 00 04 00 11 add w0, w0, #0x1

0x0000000000000a30 : 60 00 00 b9 str w0, [x3]

12 ++atomic_counter;

0x0000000000000a34 : 40 fc 5f 88 ldaxr w0, [x2]

0x0000000000000a38 : 00 04 00 11 add w0, w0, #0x1

0x0000000000000a3c : 40 fc 04 88 stlxr w4, w0, [x2]

0x0000000000000a40 : a4 ff ff 35 cbnz w4, 0xa34

因此原子版本实际上具有cbnz循环,直到__atomic_*存储成功为止。

基准测试

去做。 创建一个基准以显示原子速度较慢。

POSIX线程

main.c

#define _XOPEN_SOURCE 700

#include

#include

#include

enum CONSTANTS {

NUM_THREADS = 1000,

NUM_ITERS = 1000

};

int global = 0;

int fail = 0;

pthread_mutex_t main_thread_mutex = PTHREAD_MUTEX_INITIALIZER;

void* main_thread(void *arg) {

int i;

for (i = 0; i < NUM_ITERS; ++i) {

if (!fail)

pthread_mutex_lock(&main_thread_mutex);

global++;

if (!fail)

pthread_mutex_unlock(&main_thread_mutex);

}

return NULL;

}

int main(int argc, char **argv) {

pthread_t threads[NUM_THREADS];

int i;

fail = argc > 1;

for (i = 0; i < NUM_THREADS; ++i)

pthread_create(&threads[i], NULL, main_thread, NULL);

for (i = 0; i < NUM_THREADS; ++i)

pthread_join(threads[i], NULL);

assert(global == NUM_THREADS * NUM_ITERS);

return EXIT_SUCCESS;

}

编译并运行:

gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread

./main.out

./main.out 1

第一次运行正常,第二次运行由于缺少同步而失败。

似乎没有POSIX标准化的原子操作:UNIX可移植原子操作

在Ubuntu 18.04上测试。 GitHub上游。

GCC __atomic_*内置

对于那些没有C11的对象,您可以使用__atomic_* GCC扩展来实现原子增量。

main.c

#define _XOPEN_SOURCE 700

#include

#include

#include

#include

enum Constants {

NUM_THREADS = 1000,

};

int atomic_counter;

int non_atomic_counter;

void* mythread(void *arg) {

(void)arg;

for (int n = 0; n < 1000; ++n) {

++non_atomic_counter;

__atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST);

}

return NULL;

}

int main(void) {

int i;

pthread_t threads[NUM_THREADS];

for (i = 0; i < NUM_THREADS; ++i)

pthread_create(&threads[i], NULL, mythread, NULL);

for (i = 0; i < NUM_THREADS; ++i)

pthread_join(threads[i], NULL);

printf("atomic %d\n", atomic_counter);

printf("non-atomic %d\n", non_atomic_counter);

}

编译并运行:

gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread

./main.out

输出和生成的程序集:与“ C11线程”示例相同。

已在Ubuntu 16.04 amd64,GCC 6.4.0中测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值