C 语言的并发原语(一)

哈喽哈喽,我又来啦~

最近查资料发现C11标准C语言的标准库居然更新了,新增了C语言自己的并发原语,要知道在这之前的C语言想要并发要么依赖第三方库,要么依赖操作系统自己封装的库多么痛苦😭

现在C11标准往后都可以用C语言自身的并发原语啦~

这次帖子就先介绍C语言标准库中线程的用法

创建线程所使用的是thrd_create函数它的函数签名为int thrd_create(thrd_t *__thr, thrd_start_t __func, void *__arg);

int是它的返回值,线程创建成功返回thrd_success创建失败返回thrd_error

thrd_t* __thr是传入一个thrd_t类型的指针,用于记录所创建的线程的id

thrd_start_t __func是(int)(*)(void*)函数指针的类型别名,用于线程要执行的函数

void* __arg 线程要执行的函数的参数

等待线程线程结束使用的是thrd_join函数,它的函数签名是int thrd_join (thrd_t __thr, int *__res);

int是它的返回值,用于检查thrd_join函数是否执行成功,执行成功返回值thrd_success执行失败则返回值thrd_error

thrd_t __thr需要传入需要阻塞的线程id

int* __res是获取所阻塞的线程的返回值

#include <stdio.h>
#include <threads.h>

int
thrd_test(void* arg)
{
    printf("%s\n", (char*)arg);
    return 123;
}

int
main(int argc, char* argv[])
{
    thrd_t n, m;
    thrd_create(&n, thrd_test, "hello");
    thrd_create(&m, thrd_test, "world");

    printf("main\n");
    int nres;
    int mres;
    thrd_join(n, &nres);
    thrd_join(m, &mres);
    printf("nres %d\n", nres);
    printf("mres %d\n", mres);
}

thrd_join的作用不只是等待指定线程结束,它承担着释放指定线程所占资源的任务,如果一个线程已经分离或者和已经join过的线程则会发生未定义的行为。

创建线程后不进行join是一个危险的行为,当前程序的进程会因有线程没有释放错误的认为线程还在占用,进程将不会被系统回收资源将会产生僵尸线程或僵尸进程。

thrd_create使用的函数是thrd_start_t也就是int(*)(void*),这样的函数指针类型想要做到多参数还有多返回值会有一些麻烦,但是也不是不能做到

#include <stdio.h>
#include <threads.h>

int
thrd_test(void* arg)
{
    void** a     = arg;
    char*  hello = *a;
    *(int*)a[1]  = 123;

    printf("%s\n", hello);
    return 0;
}

int
main(int argc, char* argv[])
{
    thrd_t n;
    int    num;
    void*  arg[] = { "hello", &num };

    thrd_create(&n, thrd_test, arg);
    thrd_join(n, NULL);
    printf("%d\n", num); // 123
}

通过传入一个指针数组将自己想要传入的参数一起传入,这样就能做到多参数,并且再使用指针来接收函数中想要向外部传递的返回值(这在c语言中非常常见)。虽然有些麻烦但是它能很稳定的执行~毕竟写C语言在大部分时间里都是在做重复劳动trollface

C语言中的返回值通常都是用来返回错误信息,所以一般都采取在函数的形参上标注哪个形参是用来接收返回值的,比如thrd_join形参中的int* __res

好了目前就介绍到这里,C11中剩下的线程相关的函数都比较简单且明了就不过多的介绍啦,以下是线程相关的信息感兴趣的小伙伴可以自己动手试一下~

  • thrd_t 类型 线程唯一id的类型,是一个无符号长整型
  • thrd_create 函数 创建一个线程
  • thrd_join 函数 阻塞到指定线程结束为止
  • thrd_equal 函数 检查两个线程id是否为同一个线程
  • thrd_exit 函数 终止所在线程
  • thrd_yield 函数 所在的线程让出当前时间切片
  • thrd_sleep 函数 所在的线程让出当前时间切片,在指定时间后唤醒
  • thrd_current 函数 获取所在线程的id
  • thrd_detach 函数 分离指定线程,当指定线程结束后自动释放线程本身所占用的资源
  • thrd_success 枚举状态 表示线程成功执行的返回值
  • thrd_timeout 枚举状态 表示线程执行某个任务超时的返回值
  • thrd_busy 枚举状态 表示当前线程因资源暂时不可用所以执行失败的返回值
  • thrd_nomem 枚举状态 表示线程内存不足的返回值
  • thrd_error 枚举状态 表示线程错误的返回值
  • thrd_start_t 类型 是(int)(*)(void*)函数指针的类型别名,用于线程要执行的函数