Com*_*ger 53
既然你提到了fork()我假设你是一个类Unix系统,在这种情况下POSIX线程(通常称为pthreads)是你想要使用的.
具体来说,pthread_create()是创建新线程所需的函数.它的论点是:
int  pthread_create(pthread_t  *  thread, pthread_attr_t * attr, void *
   (*start_routine)(void *), void * arg);
第一个参数是返回指向线程id的指针.第二个参数是线程参数,除非您想要以特定优先级启动线程,否则它可以为NULL.第三个参数是线程执行的函数.第四个参数是执行时传递给线程函数的单个参数.
Bra*_*non 12
AFAIK,ANSI C没有定义线程,但有各种库可用.
如果您在Windows上运行,请链接到msvcrt并使用_beginthread或_beginthreadex.
如果您在其他平台上运行,请查看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
主文件
#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\n", atomic_counter);
    printf("non-atomic %d\n", non_atomic_counter);
}
编译并运行:
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 <+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 <atomic_counter>
所以我们看到原子增量是在带有f0锁前缀的指令级别完成的。
在aarch64-linux-gnu-gcc8.2.0 中,我们得到了:
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 指令完成所有这些操作。
这类似于我们使用 C++ 得到的std::atomic:std::atomic 究竟是什么?
基准
去做。Crate 一个基准来表明 atomic 更慢。
POSIX 线程
主文件
#define _XOPEN_SOURCE 700
#include <assert.h>
#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;
}
编译并运行:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out
./main.out 1
第一次运行正常,第二次由于缺少同步而失败。
似乎没有 POSIX 标准化的原子操作:UNIX Portable Atomic Operations
在 Ubuntu 18.04 上测试。GitHub 上游.
GCC__atomic_*内置
对于那些没有 C11 的人,您可以使用__atomic_*GCC 扩展实现原子增量。
主文件
#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\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 中测试。
线程不是C标准的一部分,因此使用线程的唯一方法是使用一些库(例如:Unix/Linux中的POSIX线程,_beginthread/_beginthreadex,如果你想使用该线程的C-runtime或只是CreateThread Win32 API)
| 归档时间: | 
 | 
| 查看次数: | 65421 次 | 
| 最近记录: |