Linux 线程控制

在上一篇文章中讲述了线程的概念和内存资源的分配,线程是一个很重要的知识,除了明白概念性的知识,了解其使用也是十分重要的。

与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以 "pthread_" 打头的,使用它们所需要引入的头文件是<pthread.h>,而且链接时需要使用编译器命令的 "-lpthread"选项。

创建线程

创建一个线程要使用到的函数叫做 pthread_create。其原型为:

cpp 复制代码
int pthread_create(pthread_t *restrict thread,
                   const pthread_attr_t *restrict attr,
                   void *(*start_routine)(void *),
                   void *restrict arg);
  • pthread_t *restrict thread 输出参数,成功后存放新线程 ID。
  • const pthread_attr_t *restrict attr 线程属性配置,传NULL使用默认属性。
  • void *(*start_routine)(void *) 线程入口函数指针:接收void*,返回void*
  • void *restrict arg 传递给线程入口函数的唯一参数。
  • 返回值:成功返回 0,失败返回错误码。

这里是一个简单的例子:

cpp 复制代码
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<iostream>

void* routine(void* arg)
{
    std::cout << "我是一个新线程" << std::endl;
    sleep(1);
    return nullptr;
}

int main()
{
    pthread_t tid;
    int ret = pthread_create(&tid,nullptr,routine,nullptr);
    if(ret != 0)
    {
        perror("pthread_create failed");
        return -1;
    }
    sleep(1);
    std::cout << "我是主线程" << std::endl;

    return 0;
}

在 Linux 下运行之后:

对比传统函数的:成功返回 0,失败返回 -1,并且对全局变量 error 赋值以示错误。pthread 函数没有使用这种方法,它不设置全局变量 error 而是将它直接返回,因为读取返回值要比读取线程内的 error 变量的开销要小。

TID获取

我们说过线程是轻量级进程,进程有一个标识其唯一性的东西叫做 PID ,线程同样也有,叫做 TID,上面代码中把线程ID设置为 tid 也是为了和这里对应起来。

在进程中,我们通过 getppid 和 getppid 来分别获取父子进程的 PID。而在线程中,没有父子之分,都是通过 函数 pthread_self 来获取线程的 TID 的。

函数原型:

cpp 复制代码
pthread_t pthread_self(void);

可以看到这是一个无参的函数,返回值就是调用当前函数的线程ID。

例子:

我们直接在上面的那个例子最后打印这个函数的返回值,就可以得结果:

线程终止

终止线程的方法有很多种,回忆进程相关内容,我们可能想到用exit(),这当然可以终止线程,但是进程也会被终止掉,用 return 会导致相同的结果。

如果我们想要只终止当前线程,可以让那个线程调用 pthread_exit 终止自己。

cpp 复制代码
void pthread_exit(void *retval);
  • retvalvoid* 类型指针,代表线程退出返回值

pthread_exit 是 "害己" 的,如果想要 "害人" ,可以使用 pthread_cancel 终止同一进程中的另一个线程。

cpp 复制代码
int pthread_cancel(pthread_t thread);
  • threadpthread_t 类型,目标要取消的线程 ID (由 pthread_create 输出得到)。

线程等待

线程也需要像进程那样被等待,虽然没有进程那样的僵尸状态,但是会类似僵尸,导致内存泄漏,所以线程等待是十分必要的。

使用到的函数是 pthread_join

原型:

cpp 复制代码
int pthread_join(pthread_t thread, void **retval);
  • pthread_t thread 是要等待的目标线程 ID,即 pthread_create 输出的线程标识。
  • void **retval 输出型二级指针,用于接收线程退出返回值。

使用这个函数时,线程不同的终止方式不同,retval 的内容会不同:

线程分离

默认情况下,新创建的线程是 joinable (可等待的),线程退出后,需要进行 pthread_join 等待,但如果我们不关心线程的返回值,join 反而成为了一种负担,我们可以告诉系统,当线程退出时,自动释放线程资源,这就是线程分离

要实现线程分离,使用到的函数是 pthread_detach

原型:

cpp 复制代码
int pthread_detach(pthread_t thread);

参数就不需要解释了。