由于协程的优势需要借助多线程发挥,因此实现协程库之前先动手封装一个自己的轻量线程库

其实就是对POSIX各个接口的封装,让它和项目更统一一些,刚好也可以学习一下多线程编程

POSIX线程库基本接口

pthread_create

函数原型如下

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
  • thread:指向 pthread_t 类型的指针,用于存储新创建线程的 ID。
    这里要注意,pthread_t实际上是unsigned long int,这个指针本来就是专门用来存储linux中的tid

  • attr:指向 pthread_attr_t 类型的指针,用于设置线程的属性(如栈大小、线程分离状态等)。如果为 nullptr,则使用默认属性。大部分情况直接用nullptr即可

  • start_routine:线程的启动函数,线程将从这个函数开始执行。

  • arg:传递给启动函数的参数。

这个函数最终会调用clone系统调用,创建一个新的线程,整个过程会涵盖分配栈空间、分配TCB等等必要的工作

返回时:

  • 线程库将新线程的 ID 存储到 thread 指针指向的位置。

  • 如果创建成功,返回 0;否则返回错误码。

通过thread指针指向的ID来标识新创建的线程

pthread_join

函数原型

int pthread_join(pthread_t thread, void **retval);

功能一句话概括:阻塞调用它的线程,直到thread对应的线程结束时被唤醒

如果需要返回值,那就用retval接收,不需要就设为nullptr。

这个函数返回0时表示成功。

pthread_detach

函数原型

int pthread_detach(pthread_t thread);

将线程设置为分离状态,若一个线程处于分离状态,线程结束时不会保留其资源(如栈空间、返回值等),而是由内核直接回收

总结

实际上,最重要的接口就是pthread_create,而后面两个函数都是为了保证内存安全的。换句话说,后面两个函数是为了保证目标线程在结束时资源被正确回收。如果线程既没有被 pthread_join 等待,也没有被 pthread_detach 设置为分离状态,线程结束时会保留其资源,可能导致资源泄漏

分离状态

有了上面的介绍,对线程声明周期有了基本理解后,再来看分离状态就好理解很多了:

非分离状态(Joinable)

  • 这是线程的默认状态。

  • 当线程结束时,它的资源(如栈空间、线程控制块等)不会立即被回收。

  • 线程的资源会一直保留,直到另一个线程调用 pthread_join 来回收这些资源

  • 如果线程是非分离状态,且没有线程调用 pthread_join,那么线程的资源会一直占用,可能导致资源泄漏。

分离状态(Detached)

  • 当线程被设置为分离状态时,线程结束时会自动回收其资源。

  • 分离状态的线程不能被 pthread_join 等待。

  • 如果线程是分离状态,那么它结束时会立即释放其资源,而不需要其他线程显式调用 pthread_join