news 2026/7/14 19:55:42

二、线程操作

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
二、线程操作

注意

使用pthread系列函数编译时,必须手动链接线程库:

gcc test.c-otest-pthread

不加-pthread会出现编译 / 运行异常。

为什么必须加-pthread
-pthread不只是单纯链接-lpthread,它同时做两件事:
链接阶段:链接 libpthread 线程库,提供 pthread_create / pthread_self / pthread_exit 等函数的实现;
编译预处理阶段:定义宏、调整线程安全的标准库行为(如 errno 线程局部存储、锁实现)。

线程操作

(1)pthread_create函数

1. 函数介绍

1. 函数原型

#include<pthread.h>intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);

2. 返回值

  • 成功:返回 0;
  • 失败:返回对应错误编号 (不是之前说的 errno ,获取错误号信息strerror),*thread 数据无效。

3. 参数

  • pthread_t *thread
    输出参数:创建成功后,新线程 ID 会存入该指针指向内存;创建失败时,*thread 内容未定义。
  • const pthread_attr_t *attr
    线程属性结构体指针:
    • 非 NULL:使用结构体中自定义属性创建线程,结构体需先用pthread_attr_init()初始化;
    • NULL:采用系统默认线程属性。
  • void *(*start_routine)(void *)
    线程入口函数,线程启动后执行该函数,返回值为 void*。
  • void *arg
    传递给入口函数start_routine的唯一参数。

4. 作用
在调用进程中创建并启动一条新线程。

2. 样例 (创建一个子线程)

准备一个 pthread_create.c 文件

#include<stdio.h>#include<string.h>#include<pthread.h>#include<sys/types.h>#include<unistd.h>void*handle(void*arg){printf("子线程已创建...\n");printf("参数arg: %d\n",*(int*)arg);returnNULL;}intmain(){// 创建新线程pthread_tthread;intnum=10;inttid=pthread_create(&thread,NULL,handle,(void*)&num);if(tid!=0){char*str=strerror(tid);printf("%s\n",str);}for(inti=0;i<3;i++){printf("pid: %d, i: %d\n",getpid(),i);}sleep(1);return0;}


注意上面的代码部分只有handle函数部分是子线程独立运行的代码段。

(2)pthread_self函数

1. 函数介绍

1. 函数原型

#include<pthread.h>pthread_tpthread_self(void);

2. 参数:无参数。

3. 返回值
该函数调用永远成功,返回调用者线程的 ID(类型为 pthread_t)。

4. 作用
获取当前调用进程的 ID 。

(3)pthread_exit函数

1. 函数介绍

1. 函数原型

#include<pthread.h>voidpthread_exit(void*retval);

2. 参数

  • retval:线程退出返回值指针
    • 作用:存放线程结束后要传递给其他线程的返回数据;
    • 接收方式:其他线程调用pthread_join(&tid, &res)res会接收该指针。
    • 取值规则:
      • 若不需要返回数据,传 NULL;
      • 若要传递数据,不能传入栈局部变量地址(线程销毁后栈会释放,内存失效),建议传全局变量 / 堆内存地址

3. 返回值
函数无返回值,调用后当前线程立刻终止,代码不会回到调用 pthread_exit 的下一行继续执行。

4. 作用
终止一个线程,在哪个线程中调用,就表示终止哪个线程。

2. 样例

创建一个 pthread_exit.c 文件

#include<stdio.h>#include<string.h>#include<pthread.h>#include<sys/types.h>#include<unistd.h>void*rollback(void*arg){printf("child thread id : %ld\n",pthread_self());returnNULL;}intmain(){// 创建新线程pthread_ttid;intarg=10;intret=pthread_create(&tid,NULL,rollback,(void*)&arg);if(ret!=0){char*errstr=strerror(ret);printf("error : %s\n",errstr);}// 主线程for(inti=1;i<3;i++){printf("i = %d\n",i);}printf("tid: %ld, main thread id: %ld\n",tid,pthread_self());// 终止主线程pthread_exit(NULL);printf("main thread die...\n");return0;}


可以看到 “main thread die…” 并没有输出,因此可以得到终结线程后的代码将不会执行。

(4)pthread_join函数

1. 函数介绍

1. 函数原型

#include<pthread.h>intpthread_join(pthread_tthread,void**retval);

2. 参数

  • pthread_t thread
    • 待等待的目标线程 ID;
    • 限制:该线程必须是 joinable(默认新建线程都是 joinable,调用 pthread_detach 后不可 join)。
  • void **retval
    • 输出型二级指针,用于接收目标线程的退出返回值;
    • 传 NULL:代表不需要接收线程退出结果;
    • 非空:会写入pthread_exit()传入的指针,线程被取消则写入PTHREAD_CANCELED

3. 返回值

  • 成功:返回 0;
  • 失败:返回对应错误编号。

4. 作用
该函数会阻塞等待参数thread指定的线程终止。

2. 样例

创建一个 pthread_join.c 文件

#include<stdio.h>#include<string.h>#include<pthread.h>#include<sys/types.h>#include<unistd.h>intnum=10;//这里一定要用全局变量(存在数据段)或者堆内存,线程结束栈内存会被回收void*rollback(void*arg){printf("child thread id : %ld\n",pthread_self());sleep(3);// 测试pthread_join是否阻塞pthread_exit((void*)&num);// 等同于(void *)&num}intmain(){// 创建新线程pthread_ttid;intarg=10;intret=pthread_create(&tid,NULL,rollback,(void*)&arg);if(ret!=0){char*errstr=strerror(ret);printf("error : %s\n",errstr);}// 主线程for(inti=1;i<3;i++){printf("i = %d\n",i);}printf("tid: %ld, main thread id: %ld\n",tid,pthread_self());// 阻塞等待回收子线程void*thread_val;pthread_join(tid,&thread_val);printf("main thread rece: %d\n",*(int*)thread_val);// 终止主线程pthread_exit(NULL);printf("main thread die...\n");return0;}

(5)pthread_detach函数

1. 函数介绍

1. 函数原型

#include<pthread.h>intpthread_detach(pthread_tthread);

2. 参数

  • pthread_t thread
    • 目标线程 ID,指定要设置为分离态的线程;
    • 限制:该线程当前必须是可汇合 (joinable) 状态,不能已经被分离。

3. 返回值

  • 成功:返回 0;
  • 失败:返回对应错误编号。

4. 作用

  • 修改线程属性为分离态,改变资源回收规则
    默认新建线程是 joinable,退出后资源残留,必须pthread_join回收;
    detach 之后线程一旦结束,操作系统自动释放线程栈、线程管理结构体,不会内存泄漏。
  • 解除 “必须等待” 的限制
    主线程无需阻塞等待该线程执行完毕,二者完全异步运行。

2. 样例

创建一个 pthread_detach.c 文件

#include<stdio.h>#include<string.h>#include<pthread.h>#include<sys/types.h>#include<unistd.h>intnum=10;//这里一定要用全局变量(存在数据段)或者堆内存,线程结束栈内存会被回收void*rollback(void*arg){printf("child thread id : %ld\n",pthread_self());sleep(3);// 测试pthread_detach是否阻塞return(void*)&num;}intmain(){// 创建新线程pthread_ttid;intarg=10;intret=pthread_create(&tid,NULL,rollback,(void*)&arg);if(ret!=0){char*errstr=strerror(ret);printf("error : %s\n",errstr);}// 主线程for(inti=1;i<3;i++){printf("i = %d\n",i);}printf("tid: %ld, main thread id: %ld\n",tid,pthread_self());// 设置子线程分离,子线程分离后,子线程结束时对应的资源就不需要主线程释放ret=pthread_detach(tid);if(ret!=0){char*errstr=strerror(ret);printf("error1 : %s\n",errstr);}// 设置分离后,对分离的子线程进行连接 pthread_join()void*thread_val;ret=pthread_join(tid,&thread_val);if(ret!=0){char*errstr=strerror(ret);printf("error2 : %s\n",errstr);}printf("main thread rece: %d\n",*(int*)thread_val);// 终止主线程pthread_exit(NULL);printf("main thread die...\n");return0;}


可以看到将线程分离后再次进行连接将会错误。

(6)pthread_cancel函数

1. 函数介绍

1. 函数原型

#include<pthread.h>intpthread_cancel(pthread_tthread);

2. 参数

  • pthread_t thread
    • 目标线程 ID,代表要发送取消信号的线程;
    • 支持对自身线程、其他线程发送取消请求。

3. 返回值

  • 成功:返回 0;
  • 失败:返回对应错误编号。

4. 作用
pthread_cancel()函数向thread指定的目标线程发送取消请求。

目标线程是否响应、何时响应这个取消请求,由该线程自身的两个属性控制:state (取消使能状态) 和 type (取消类型)。

5. 两大关键控制属性(目标线程自身控制是否响应取消)
① 取消使能状态pthread_setcancelstate函数设置 state

  • PTHREAD_CANCEL_ENABLE(默认):接收并处理取消请求;
  • PTHREAD_CANCEL_DISABLE:忽略取消请求,请求排队暂存,直到重新开启。

② 取消类型pthread_setcanceltype函数设置 type

  • PTHREAD_CANCEL_DEFERRED(默认,延迟取消)
    不会立刻终止,仅在线程调用取消点函数(read/write/pthread_join/sleep等系统 IO、同步函数)时才触发取消流程;
    业务计算循环、纯 CPU 运算代码段不会被中途打断,安全。
  • PTHREAD_CANCEL_ASYNCHRONOUS(异步取消)
    线程任意代码位置都可能被随时终止,风险极高,极易造成资源泄漏、死锁,极少使用。

6. 线程被取消后的固定执行流程

  1. 逆序执行所有清理函数pthread_cleanup_push
  2. 执行线程私有数据 TSD 析构函数;
  3. 线程正式终止退出。

2. 样例

创建一个pthread_cancel.c 文件

#include<stdio.h>#include<string.h>#include<pthread.h>#include<sys/types.h>#include<unistd.h>intnum=10;//这里一定要用全局变量(存在数据段)或者堆内存,线程结束栈内存会被回收void*rollback(void*arg){printf("child thread id : %ld\n",pthread_self());sleep(1);for(inti=1;i<100;i++){printf("child pthread i: %d\n",i);}return(void*)&num;}intmain(){// 创建新线程pthread_ttid;intarg=10;intret=pthread_create(&tid,NULL,rollback,(void*)&arg);if(ret!=0){char*errstr=strerror(ret);printf("error : %s\n",errstr);}// 设置子线程分离,子线程分离后,子线程结束时对应的资源就不需要主线程释放pthread_detach(tid);// 终止指定子进程pthread_cancel(tid);for(inti=1;i<3;i++){printf("i = %d\n",i);}printf("tid: %ld, main thread id: %ld\n",tid,pthread_self());// 终止主线程pthread_exit(NULL);printf("main thread die...\n");return0;}


可以看到子线程并没有输出循环体的内容就被父进程给终止了。这是因为sleep函数本身是取消点函数,当取消请求队列中有取消信号则会终止该线程。

线程属性

线程属性类型pthread_attr_t

(1) 初始化、释放线程属性资源

#include<pthread.h>intpthread_attr_init(pthread_attr_t*attr);intpthread_attr_destroy(pthread_attr_t*attr);

描述
pthread_attr_init()函数会使用默认值初始化attr指向的线程属性对象。调用该函数后,可以通过配套系列函数单独修改属性对象里的各项配置,之后该属性对象可以重复用于一次或多次pthread_create()创建线程。

对一个已经初始化完成的属性对象再次调用pthread_attr_init(),程序行为属于未定义。

当线程属性对象不再使用时,应当调用pthread_attr_destroy()销毁释放内部资源。销毁属性对象不会对以往依靠该属性创建出的线程产生任何影响。

属性对象被销毁后,可以再次调用pthread_attr_init()重新初始化复用;对已经销毁、未重新初始化的属性对象做任何其他操作,结果都是未定义行为。

返回值
两个函数调用成功均返回 0;执行失败时返回非 0 错误码。

(2) 设置、获取线程属性

#include<pthread.h>intpthread_attr_getdetachstate(constpthread_attr_t*attr,int*detachstate);intpthread_attr_setdetachstate(pthread_attr_t*attr,intdetachstate);

描述
pthread_attr_setdetachstate()函数用于设置线程属性对象attr的分离状态属性,参数detachstate指定目标状态。该属性决定了使用此属性对象创建出的线程,默认是可汇合(joinable)还是分离(detached) 状态。

detachstate仅支持以下两个宏取值:

  1. PTHREAD_CREATE_DETACHED
    使用该属性创建的线程,直接为分离态。
  2. PTHREAD_CREATE_JOINABLE
    使用该属性创建的线程,为可汇合态。

刚通过pthread_attr_init()初始化完成的线程属性对象,分离状态默认值PTHREAD_CREATE_JOINABLE

pthread_attr_getdetachstate()函数会将属性对象attr中存储的分离状态,存入detachstate指针指向的内存中返回。

返回值:
两个函数调用成功均返回 0;执行失败时,返回非 0 的错误码。

(3) 样例

创建一个 pthread_attr.c 文件

#include<stdio.h>#include<string.h>#include<pthread.h>#include<sys/types.h>#include<unistd.h>intnum=10;//这里一定要用全局变量(存在数据段)或者堆内存,线程结束栈内存会被回收void*rollback(void*arg){printf("child thread id : %ld\n",pthread_self());sleep(1);return(void*)&num;}intmain(){// 设置线程属性pthread_attr_tattr;pthread_attr_init(&attr);// 初始化intret=pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);// 设置由该属性创建的线程为分离态if(ret!=0){char*errstr=strerror(ret);printf("error1 : %s\n",errstr);}// 查看新建的属性intdetachstate;pthread_attr_getdetachstate(&attr,&detachstate);if(detachstate==PTHREAD_CREATE_DETACHED){printf("新建属性: detached state\n");}elseif(detachstate==PTHREAD_CREATE_JOINABLE){printf("新建属性: joinable state\n");}// 创建新线程pthread_ttid;intarg=10;ret=pthread_create(&tid,&attr,rollback,(void*)&arg);// 创建子线程的同时设置属性if(ret!=0){char*errstr=strerror(ret);printf("error2 : %s\n",errstr);}printf("tid: %ld, main thread id: %ld\n",tid,pthread_self());pthread_attr_destroy(&attr);// 释放线程属性资源// 终止主线程pthread_exit(NULL);printf("main thread die...\n");return0;}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 19:55:02

第 7 章 函数与递归

1. 简单算术表达式求值#include<bits/stdc.h>//万能头 using namespace std;//使用std命名空间 int main(){//主函数int n1,n2;char ch;//n1为第一个运算数&#xff0c;n2为第二个运算数&#xff0c;ch为运算符cin>>n1>>ch>>n2;//输入&#xff1a;由于…

作者头像 李华
网站建设 2026/7/14 19:49:13

SL6Pro与SDR6联动:实时频谱分析与软件无线电的协同应用

在实际无线电信号分析工作中&#xff0c;单一设备往往难以同时满足高精度测量和实时监听的需求。SL6Pro作为一款实时频谱分析仪&#xff0c;能够覆盖9kHz至40GHz的宽频率范围&#xff0c;提供精确的频谱测量&#xff1b;而SDR6软件无线电接收机则擅长信号的实时捕获和解调。将两…

作者头像 李华
网站建设 2026/7/14 19:44:42

华为MetaERP AI 本体论:设计哲学、底层实现、ERP 全流程落地(财务 / PTP/OTC 端到端打通)一、核心基础:什么是 AI 本体论(面向企业 ERP 场景,非纯哲学)1. 本体论基

AI 本体论&#xff1a;设计哲学、底层实现、ERP 全流程落地&#xff08;财务 / PTP/OTC 端到端打通&#xff09;一、核心基础&#xff1a;什么是 AI 本体论&#xff08;面向企业 ERP 场景&#xff0c;非纯哲学&#xff09;1. 本体论基础定义AI 领域本体&#xff08;Ontology&am…

作者头像 李华
网站建设 2026/7/14 19:44:20

为什么92%的中大型企业放弃纯API方案?ChatGPT技术栈选型必须回答的7个合规性问题(GDPR+等保2.0+信创适配清单)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;ChatGPT技术选型的合规性危机本质 当企业将ChatGPT类大语言模型集成至核心业务系统时&#xff0c;表面是效率跃升&#xff0c;深层却是数据主权、算法透明度与监管适配性的三重失衡。合规性危机并非源于模型性…

作者头像 李华