我已经在C语言中使用fork()启动了另一个进程。 如何启动新线程?
自从您提到fork()以来,我假设您使用的是类似Unix的系统,在这种情况下,您要使用POSIX线程(通常称为pthread)。
具体来说,pthread_create()是创建新线程所需的函数。它的参数是:
1 2
| int pthread_create(pthread_t * thread, pthread_attr_t * attr, void *
(*start_routine)(void *), void * arg); |
第一个参数是返回的线程ID指针。第二个参数是线程参数,除非您想以特定优先级启动线程,否则它可以为NULL。第三个参数是线程执行的函数。第四个参数是执行时传递给线程函数的单个参数。
AFAIK,ANSI C没有定义线程,但是有各种可用的库。
如果您在Windows上运行,请链接到msvcrt并使用_beginthread或_beginthreadex。
如果您在其他平台上运行,请签出pthreads库(我确定还有其他)。
线程不是C标准的一部分,因此使用线程的唯一方法是使用某些库(例如:Unix / Linux中的POSIX线程,_beginthread / _beginthreadex,如果您想使用该线程中的C运行时或仅使用CreateThread Win32 API)
pthreads是一个好的开始,请看这里
C11线程+ C11 atomic_int
已添加到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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
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
", atomic_counter);
printf("non-atomic %d
", non_atomic_counter);
} |
GitHub上游。
编译并运行:
1 2
| gcc -ggdb3 -std=c11 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out |
可能的输出:
1 2
| atomic 10000
non-atomic 4341 |
由于跨线程访问非原子变量,非原子计数器很可能小于原子计数器。
另请参阅:如何在C中进行原子增量和获取?
拆卸分析
拆卸:
1
| gdb -batch -ex"disassemble/rs mythread" main.out |
包含:
1 2 3 4 5
| 17 ++non_atomic_counter;
0x00000000004007e8 <+8>: 83 05 65 08 20 00 01 addl $0x1,0x200865(%rip) # 0x601054 <non_atomic_counter>
18 __atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST);
0x00000000004007ef <+15>: f0 83 05 61 08 20 00 01 lock addl $0x1,0x200861(%rip) # 0x601058 |
因此我们看到原子增量是在指令级别使用f0锁定前缀完成的。
使用aarch64-linux-gnu-gcc 8.2.0,我们得到的是:
1 2 3 4 5 6 7 8 9 10
| 11 ++non_atomic_counter;
0x0000000000000a28 <+24>: 60 00 40 b9 ldr w0, [x3]
0x0000000000000a2c <+28>: 00 04 00 11 add w0, w0, #0x1
0x0000000000000a30 <+32>: 60 00 00 b9 str w0, [x3]
12 ++atomic_counter;
0x0000000000000a34 <+36>: 40 fc 5f 88 ldaxr w0, [x2]
0x0000000000000a38 <+40>: 00 04 00 11 add w0, w0, #0x1
0x0000000000000a3c <+44>: 40 fc 04 88 stlxr w4, w0, [x2]
0x0000000000000a40 <+48>: a4 ff ff 35 cbnz w4, 0xa34 <mythread+36> |
因此,原子版本实际上具有一个cbnz循环,该循环一直运行到stlxr存储成功为止。请注意,ARMv8.1可以使用一条LDADD指令完成所有这些操作。
基准测试
去做。创建一个基准以显示原子速度较慢。
POSIX线程
main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #define _XOPEN_SOURCE 700
#include
#include <stdlib.h>
#include <pthread.h>
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;
} |
编译并运行:
1 2 3
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #define _XOPEN_SOURCE 700
#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
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
", atomic_counter);
printf("non-atomic %d
", non_atomic_counter);
} |
编译并运行:
1 2
| 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中测试。
检出pthread(POSIX线程)库。