当前位置: 首页 > news >正文

Linux-线程控制

线程等待

pthread_join()

pthread_join  是 Linux 系统中用于线程同步的重要函数,主要作用是等待指定线程结束并回收其资源。

基本功能

- 阻塞当前调用线程,直到目标线程执行结束。
- 回收目标线程的资源,避免产生“僵尸线程”。
- 可选地获取目标线程的返回值。

函数原型

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

-  thread :需要等待的目标线程 ID(由  pthread_create  返回)。
-  retval :用于存储目标线程的返回值(若不需要,可设为  NULL )。
- 返回值:成功返回 0,失败返回非 0 错误码。


使用示例

#include <pthread.h>
#include <stdio.h>void *thread_func(void *arg) {printf("子线程执行\n");return (void *)100; // 子线程返回值
}int main() {pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);void *ret;pthread_join(tid, &ret); // 等待子线程结束,获取返回值printf("子线程返回值:%d\n", (int)ret);return 0;
}

注意事项

- 每个线程只能被  pthread_join  一次,多次调用会出错。
- 若不调用  pthread_join  且未设置线程分离( pthread_detach ),线程结束后资源不会被回收,会成为僵尸线程。
- 若目标线程已结束, pthread_join  会立即返回并回收资源。

线程退出

方式1

return 

方式2

pthread_exit()pthread_exit  是 Linux 中用于线程退出的函数,定义在  <pthread.h>  头文件中,用于终止当前线程的执行并返回退出状态。

基本用法

- 函数原型: void pthread_exit(void *retval); 
- 参数  retval :指向线程退出状态的指针,其他线程可通过  pthread_join  获取该值。
- 作用:终止调用线程,释放线程资源(但不会自动释放线程创建时分配的堆内存等,需手动管理)。


关键特点

- 仅终止当前线程:与  exit  不同, pthread_exit  只结束调用它的线程,不影响进程中其他线程的执行。
- 退出状态传递:通过  retval  传递的状态需为全局变量或动态分配的内存(避免栈内存被释放),否则其他线程可能获取到无效值。
- 与  return  的区别:在线程函数中使用  return  与  pthread_exit  效果类似,但  pthread_exit  更灵活(可在函数中任意位置调用)。

示例

#include <pthread.h>
#include <stdio.h>void *thread_func(void *arg) {int *result = malloc(sizeof(int));*result = 100;pthread_exit(result); // 线程退出并返回结果
}int main() {pthread_t tid;void *ret;pthread_create(&tid, NULL, thread_func, NULL);pthread_join(tid, &ret); // 获取线程退出状态printf("线程返回值:%d\n", *(int*)ret);free(ret); // 释放动态分配的内存return 0;
}

上述示例中,线程通过  pthread_exit  返回动态分配的结果,主线程通过  pthread_join  获取并释放内存。

方式3

pthread_cancel()(线程的取消)

pthread_cancel  是 Linux 中用于取消线程执行的函数,属于 POSIX 线程库(pthread)的一部分,其作用是请求终止指定的线程。

基本语法

#include <pthread.h>
int pthread_cancel(pthread_t thread);

- 参数  thread :目标线程的 ID(由  pthread_create  返回)。
- 返回值:成功返回 0,失败返回非 0 错误码(如  ESRCH  表示线程不存在)。

核心特点

1. 请求而非强制终止
pthread_cancel  只是发送一个“取消请求”,并非立即终止线程。线程是否响应、何时响应,取决于其“取消状态”和“取消类型”。
2. 取消状态(可通过  pthread_setcancelstate  设置)
-  PTHREAD_CANCEL_ENABLE (默认):线程允许响应取消请求。
-  PTHREAD_CANCEL_DISABLE :线程忽略取消请求,直到状态改为允许。
3. 取消类型(可通过  pthread_setcanceltype  设置,仅当状态为允许时有效)
-  PTHREAD_CANCEL_DEFERRED (默认):线程在“取消点”(如  sleep 、 read  等系统调用)处响应请求。
-  PTHREAD_CANCEL_ASYNCHRONOUS :线程立即响应请求(较少使用,可能导致资源未释放)。

注意事项

- 线程被取消后,资源(如锁、内存)需通过“线程清理函数”( pthread_cleanup_push / pthread_cleanup_pop )释放,避免泄漏。
- 并非所有函数都是取消点,可通过  pthread_testcancel  主动检查取消请求(手动创建取消点)。


示例:通过  pthread_cancel  取消一个延迟响应的线程,需确保线程在取消点处被终止。

注意

线程间的通信不仅仅只可以交流传输字符串或整数

这段代码展示了如何在多线程中通过指针传递自定义对象( Request  和  Response ),实现线程间的数据交互。主要功能是创建一个子线程,计算从  start_  到  end_  的整数和,并通过  Response  对象返回结果。

代码解析

1. 自定义类

-  Request :封装线程的输入参数(计算范围  start_ / end_ 、线程名称  threadname_ )。

-  Response :封装线程的输出结果(计算总和  result_ 、状态码  exitcode_ )。

2. 线程函数  sumCount 

- 接收  Request*  类型的参数,解析输入范围并循环计算总和。

- 每次循环打印当前进度(线程名+当前计算的数字),并通过  usleep(100000)  暂停0.1秒(方便观察过程)。

- 计算完成后,创建  Response  对象存储结果,释放  Request  资源,返回  Response* 。

3. 主线程  main 

- 创建  Request  对象并传入子线程,通过  pthread_create  启动线程。

- 调用  pthread_join  等待子线程结束,获取返回的  Response*  并打印结果,最后释放资源。

 class Request{public:Request(int start, int end, const string &threadname): start_(start), end_(end), threadname_(threadname){}public:int start_;int end_;string threadname_;};class Response{public:Response(int result, int exitcode):result_(result),exitcode_(exitcode){}public:int result_;   // 计算结果int exitcode_; // 计算结果是否可靠};void *sumCount(void *args) // 线程的参数和返回值,不仅仅可以用来进行传递一般参数,也可以传递对象!!{Request *rq = static_cast<Request*>(args); //  Request *rq = (Request*)argsResponse *rsp = new Response(0,0);for(int i = rq->start_; i <= rq->end_; i++){cout << rq->threadname_ << " is runing, caling..., " << i << endl;rsp->result_ += i;usleep(100000);}delete rq;return rsp;}int main(){pthread_t tid;Request *rq = new Request(1, 100, "thread 1");pthread_create(&tid, nullptr, sumCount, rq);void *ret;pthread_join(tid, &ret);Response *rsp = static_cast<Response *>(ret);cout << "rsp->result: " << rsp->result_ << ", exitcode: " << rsp->exitcode_ << endl;delete rsp;return 0;}

线程及轻量化进程底层实现逻辑运用的是clone

clone 

 在 Linux 中, clone  是一个系统调用,主要用于创建新的进程(或线程),与  fork  相比,它提供了更精细的控制能力,可以指定新进程与父进程共享哪些资源。
 
clone  的核心特点

- 灵活的资源共享:通过参数可以指定新进程是否共享父进程的内存空间、文件描述符表、信号处理等资源。例如,创建线程时通常会共享内存空间,而创建独立进程时则不共享。
- 与  fork  的关系: fork  可以看作是  clone  的一种特殊情况( clone  省略部分参数时的简化版), fork  会复制父进程的几乎所有资源,而  clone  可按需共享。

主要用途

- 创建线程(如 POSIX 线程 pthread 底层可能使用  clone ,共享内存空间)。
- 创建具有特定资源共享策略的进程,满足特殊场景需求(如轻量级进程 LWP)。

简单来说, clone  是 Linux 中一个更底层、更灵活的进程/线程创建工具,通过控制资源共享粒度,适应不同的并发场景。

由于clone相对于用户而言使用过于复杂,因此 Clone被包装成库供用户使用,开发者将要调用的函数指针和栈区暴露出来给用户使用,由于操作系统要对线程进行管理,而线程共用一个动态库,因此副线程它主要存储在共享区,而主线程及进程它是存放在内核的主线程栈中

TCB

在 Linux 中,TCB(Thread Control Block,线程控制块) 是内核中用于管理线程状态的核心数据结构,类似于进程的 PCB(Process Control Block),但专门用于线程。

TCB 的主要作用

- 存储线程的基本信息,如线程 ID(TID)、状态(运行、就绪、阻塞等)。
- 记录线程的上下文(寄存器值、程序计数器等),用于线程切换时保存和恢复状态。
- 关联线程所属的进程(进程 PCB),以及线程组信息。
- 管理线程的栈指针、信号掩码、调度优先级等私有资源。

与 clone 的关系

当使用  clone  创建线程时(如指定  CLONE_THREAD 、 CLONE_VM  等标志),内核会为新线程创建一个 TCB,同时共享进程的部分资源(如内存空间)。TCB 是内核识别和调度线程的关键,确保每个线程能独立被调度,同时与同进程其他线程协作。

简单说,TCB 就是线程在内核中的“身份证”和“状态档案”,支撑线程的独立运行和管理。

TID

在 Linux 中,TID(Thread ID,线程 ID) 是用于唯一标识线程的编号,类似于进程的 PID(Process ID),但专门针对线程。

TID 的特点

- 唯一性:系统中每个线程都有一个唯一的 TID,即使是同一进程内的不同线程,TID 也互不相同。
- 与 PID 的关系:在 Linux 中,线程本质上是轻量级进程(LWP),因此 TID 在内核中与 PID 共享同一编号空间(即 TID 也是一个“进程 ID”,但属于线程级别的标识)。
- 线程组关联:同一进程的所有线程属于同一个线程组,线程组的领头线程(通常是进程创建的第一个线程)的 TID 等于进程的 PID。

作用

- 内核通过 TID 识别和调度不同的线程。
- 用户态可通过  gettid()  系统调用获取当前线程的 TID,用于线程管理、调试等场景(如  ps -T  命令可查看进程内的线程 TID)。


例如,一个进程包含 3 个线程,它们会有各自不同的 TID,但共享同一个进程 PID(等于领头线程的 TID)。

http://www.dtcms.com/a/277669.html

相关文章:

  • System.getenv()拿不到你配置的环境变量
  • 【Mysql作业】
  • OSPF协议特性
  • kettle从入门到精通 第九十七课 ETL之kettle kettle资源仓库的5种方式
  • Linux修炼:开发工具
  • linux-shell脚本
  • 学习环形数组ringbuffer和缓存管理buffer_manager_struct的一些思考
  • k8s:0/1 nodes are available: pod has unbound immediate PersistentVolumeClaims.
  • CSS个人笔记分享【仅供学习交流】
  • 深度学习图像分类数据集—角膜溃疡识别分类
  • INA226 数据手册解读
  • CCS-MSPM0G3507-6-模块篇-OLED的移植
  • Leetcode 3614. Process String with Special Operations II
  • 【Vue】浏览器缓存 sessionStorage、localStorage、Cookie
  • XXL-TOOL v1.5.0 发布 | Java工具类库
  • https交互原理
  • 010_学习资源与社区支持
  • cs285学习笔记(一):课程总览
  • 融合开源AI大模型与MarTech:AI智能名片与S2B2C商城小程序源码赋能数字化营销新生态
  • Boost.Asio 中 io_context 类 post 和 dispatch的区别
  • 启动Tomcat报错:A child container failed during start
  • MCP 服务开发到发布
  • 更换docker工作目录
  • MongoDB对接SpringBoot【大数据存储】
  • Hashtable 与 HashMap 的区别笔记
  • 利用DeepSeek证明立体几何题目
  • Flink学习笔记:整体架构
  • Vue 3 动态ref问题
  • 第十五篇:Python操作Excel速成:读写单元格、样式与公式,你的第一个数据自动化脚本!告别手动录入!
  • 002大模型基础知识