一,线程的概念

进程是操作系统分配的最小单位,而线程是进程内部的执行单元,是操作系统CPU调度的最小单位,简单来说就是一个进程可以有多个线程。

二,与进程对比

维度 进程 线程
资源 独立的地址空间、文件描述符等系统资源 共享所属进程的地址空间和资源
调度 进程是资源分配的基本单位 线程是 CPU 调度的基本单位
开销 创建 / 切换开销大(需复制页表等) 创建 / 切换开销小(共享资源,仅保存少量寄存器)
通信 需通过 IPC(管道、消息队列、共享内存) 可直接读写共享变量,通信简单但需同步
健壮性 进程崩溃不影响其他进程 一个线程崩溃会导致整个进程退出

应用场景:

  1. 进程适用于资源隔离要求高,稳定性优先的场景,如Web服务器的工作进程,数据库的多进程架构
  2. 线程适用于高并发,计算密集型任务,如Web服务器的线程池,GUI程序的后台任务处理

三,多线程相关函数

1.前置条件

  • 头文件:
#include <pthread.h>
  • 编译时必须加链接库参数:
gcc main.c -o main -lpthread
  • 所有pthread函数成功返回0,失败返回错误码

2.创建线程:pthread_create

作用:创建一个新线程,让它开始指定的函数,参数结构如下:

int pthread_create(
    pthread_t *thread,        // 输出:存储新线程的ID
    const pthread_attr_t *attr, // 线程属性(默认填NULL)
    void *(*start_routine)(void *), // 线程要执行的函数指针,也称入口函数
    void *arg                // 传递给线程函数的参数(无参数填NULL)
);

3.等待线程结束:pthread_join

作用:阻塞等待指定线程执行完毕,并回收线程资源(避免内存泄漏),参数结构如下:

int pthread_join(
    pthread_t thread,  // 要等待的线程ID
    void **retval      // 接收线程的返回值(不接收填NULL)
);

4.线程主动退出:pthread_exit

线程自己调用,主动终止运行,可以带返回值,结构如下:

void pthread_exit(void *retval); // retval:线程返回值

ps:即使主线程退出,子线程也能继续执行

5.获取线程自身线程id:pthread_self

线程获取自己的id,类似于进程的getpid,结构如下:

pthread_t pthread_self(void);

线程属性相关函数

6.分离线程:pthread_detach

将线程设置为分离态,分离态下线程退出后系统自动回收其资源,无需调用pthread_join

结构如下:

int pthread_detach(pthread_t thread);

7.初始化线程属性:pthread_attr_init

初始化线程属性对象,将属性设置为默认值,结构如下:

int pthread_attr_init(pthread_attr_t *attr);//attr需要被初始化的线程属性对象

8.设置线程属性对象的分离状态:pthread_attr_setdetachstate

用于设置线程属性对象的分离状态,结构如下:

int pthread_attr_setdetachstate(
    pthread_attr_t *attr,  //设置分离状态的线程属性对象
    int detachstate  //用于指定线程的分离状态
                     //PTHREAD_CREATE_JOINABLE :表示将线程设置为结合态
                     //PTHREAD_CREATE_DETACHED :表示将线程设置为分离态
);

9.销毁线程属性:pthread_attr_destroy

销毁属性对象占用的资源,与初始化函数成对使用,结构如下:

int pthread_attr_destroy(pthread_attr_t *attr);

ps:销毁后的属性可以重新初始化,不影响已创建的线程。

线程取消相关函数

10.请求取消指定线程:pthread_cancel

向指定线程发送取消请求,它只是一个请求,调用成功也只是代表请求已提交,不代表线程已经终止了,结构如下:

int pthread_cancel(pthread_t thread);

11.设置取消状态:pthread_setcancelstate

设置调用线程自身的取消状态,即是否允许响应pthread_cancel的请求,参数:

参数 说明
state

新的取消状态,二选一:

PTHREAD_CANCEL_ENABLE(默认):允许响应取消请求

PTHREAD_CANCEL_DISABLE:禁止响应取消请求(请求会被暂时挂起)

oldstate 输出参数,存储修改前的旧状态(传NULL表示不需要保存旧状态)
int pthread_setcancelstate(int state, int *oldstate);

12.设置取消类型:pthread_setcanceltype

设置调用线程自身的取消类型,仅在取消状态为PTHREAD_CANCEL_ENABLE时生效,决定线程收到请求后何时终止,参数:

参数 说明
type

新的取消类型,二选一:

PTHREAD_CANCEL_DEFERRED(默认,延时取消):线程不会立刻终止,直到下一个取消点(Cancellation Point)才响应

PTHREAD_CANCEL_ASYNCHRONOUS(异步取消):线程收到请求后会 “随时终止”,风险极高

oldtype 输出参数,存储修改前的旧类型(传NULL表示不需要保存旧类型)
int pthread_setcanceltype(int type, int *oldtype);

13.取消函数的示例代码

//请求取消指定线程
/*
    请求取消指定线程取决于线程的取消状态和取消类型
    取消状态:可取消和不可取消
    取消类型:延时取消和可被立即取消(异步取消)
    三种情况:
        1. 可取消的线程+延时取消
        2. 可取消的线程+可被立即取消
        3. 不可取消的线程
*/
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

//情况1:可取消的线程+延时取消
void *thread1(void *arg)
{
    //设置线程1取消状态和取消类型
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
    while(1) {
        //结果:不会每隔1秒打印一次线程1正在运行...
        //因为线程1收到请求取消信号,线程1会在取消点进行取消
        printf("线程1正在运行...\n");
        sleep(1);
    }
    return NULL;//注意:此处返回值不可以为丢,没有实际意义
}

//情况2:可取消的线程+可被立即取消
void *thread2(void *arg)
{
    //设置线程2取消状态和取消类型
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    while(1) {
        //因为设置为了立即取消,所以线程2会立即取消
        //所以将sleep注释之后无法显示
        printf("线程2正在运行...\n");
        //sleep(1);
    }
    return NULL;//注意:此处返回值不可以为丢,没有实际意义
}

//情况3:不可取消的线程
void *thread3(void *arg)
{
    //设置线程3取消状态为不可取消
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    while(1) {
        //因为设置为了不可取消,所以线程3不会取消
        printf("线程3正在运行...\n");
        sleep(1);
    }
    return NULL;//注意:此处返回值不可以为丢,没有实际意义
}

int main(int argc,const char *argv[])
{
    //情况1:可取消的线程+延时取消
    pthread_t tid1 = 0;
    int ret = pthread_create(&tid1,NULL,thread1,NULL);
    if(ret != 0) {
        fprintf(stderr,"创建线程1失败,错误信息:%s\n",strerror(ret));
        return -1;
    }
    printf("创建线程1成功,线程1ID:%ld\n",tid1);
    //情况2:可取消的线程+可被立即取消
    pthread_t tid2 = 0;
    int ret2 = pthread_create(&tid2,NULL,thread2,NULL);
    if(ret2 != 0) {
        fprintf(stderr,"创建线程2失败,错误信息:%s\n",strerror(ret2));
        return -1;
    }
    printf("创建线程2成功,线程2ID:%ld\n",tid2);
    //情况3:不可取消的线程
    pthread_t tid3 = 0;
    int ret3 = pthread_create(&tid3,NULL,thread3,NULL);
    if(ret3 != 0) {
        fprintf(stderr,"创建线程3失败,错误信息:%s\n",strerror(ret3));
        return -1;
    }
    printf("创建线程3成功,线程3ID:%ld\n",tid3);
    //请求取消指定的线程1
    int ret_cancel = pthread_cancel(tid1);
    //只能说明请求取消成功,不能说明会被立即取消
    if(ret_cancel != 0) {
        fprintf(stderr,"请求取消线程1失败,错误信息:%s\n",strerror(ret_cancel));
        return -1;
    }
    printf("请求取消线程1成功\n");
    //请求取消指定的线程2
    int ret_cancel2 = pthread_cancel(tid2);
    if(ret_cancel2 != 0) {
        fprintf(stderr,"请求取消线程2失败,错误信息:%s\n",strerror(ret_cancel2));
        return -1;
    }
    printf("请求取消线程2成功\n");
    //请求取消指定的线程3
    int ret_cancel3 = pthread_cancel(tid3);
    if(ret_cancel3 != 0) {
        fprintf(stderr,"请求取消线程3失败,错误信息:%s\n",strerror(ret_cancel3));
        return -1;
    }
    printf("请求取消线程3成功\n");
    //阻塞等待线程退出
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    return 0;
}

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐