深入理解Linux中的线程控制:多线程编程的实战技巧

深入理解Linux中的线程控制:多线程编程的实战技巧

个人主页:chian-ocean

文章专栏-Linux

前言:

POSIX线程(Pthreads) 是一种在 POSIX 标准下定义的线程库,它为多线程编程提供了统一的接口,主要用于 UNIX 和类 UNIX 系统(如 Linux、MacOS 和 BSD 等)。POSIX 线程(Pthreads)允许程序在多个处理器上并行运行,从而提高应用程序的性能,尤其在多核处理器环境中。
在这里插入图片描述

监控线程的bash

while:;do ps -aL | head -1; ps -aL | grep thread ; sleep 1;done 

线程的控制

  • 与线程有关的函数构成了⼀个完整的系列,绝⼤多数函数的名字都是以pthread_打头的
  • 要使⽤这些函数库,要通过引⼊头⽂ pthread.h
  • 链接这些线程函数库时要使⽤编译器命令的“-lpthread”选项

线程的创建(pthread_create)

在这里插入图片描述
#include<pthread.h>intpthread_create(pthread_t *thread,const pthread_attr_t *attr,void*(*start_routine)(void*),void*arg);

参数解析:

  1. pthread_t *thread:指向 pthread_t 类型的变量,这个变量将存储新线程的 ID。
  2. const pthread_attr_t *attr:指向 pthread_attr_t 类型的指针,它包含了新线程的属性(如栈大小、调度策略等)。如果传入 NULL,则使用默认属性。
  3. void *(*start_routine)(void *):这是一个指向线程执行函数的指针,该函数接收一个 void* 类型的参数,并返回 void* 类型的结果。
  4. void *arg:这是传递给 start_routine 函数的参数,允许你向线程传递数据。

示例:

#include<iostream>#include<unistd.h>#include<pthread.h>usingnamespace std;// 线程函数void*mythread(void* args){// 创建一个循环,子线程会打印 6 次for(int i =0; i <6; i++){sleep(1);// 让线程睡眠 1 秒钟,模拟任务的执行 cout <<"Child Thread"<< endl;// 打印“Child Thread”,表示子线程在运行}returnnullptr;}// 主函数intmain(){ pthread_t tid;// 定义一个线程ID变量// 创建子线程,线程的执行函数是 mythread,其他参数为默认值pthread_create(&tid,nullptr, mythread,nullptr);// 主线程执行,循环 6 次for(int i =0; i <6; i++){ cout <<"Main Thread"<< endl;// 打印“Main Thread”,表示主线程在运行sleep(1);// 让主线程睡眠 1 秒钟}// 等待子线程完成执行pthread_join(tid,nullptr);// 阻塞等待 tid 线程执行完毕return0;// 程序正常退出}
  • pthread_create():用于创建一个新的线程,mythread 是新线程的执行函数。该函数接受参数 nullptr,表示没有传递任何参数给线程。
  • pthread_join(tid, nullptr):等待线程 tid 执行完成,pthread_join 阻塞主线程,直到子线程执行完毕。

打印结果:

在这里插入图片描述

线程退出

pthread_join

在这里插入图片描述
#include<pthread.h>intpthread_join(pthread_t thread,void**retval);
参数说明:
  • pthread_t thread:要等待结束的线程 ID。
  • void **retval:用于存储线程返回值的指针。如果不需要返回值,可以将其设置为 NULL
功能描述:
  • pthread_join() 函数使得调用该函数的线程(通常是主线程)阻塞,直到指定的线程(由 thread 参数指定)执行完毕。
  • 当线程结束后,系统会回收该线程的资源。如果 retval 不为 NULL,线程的返回值将被存储在 retval 指向的位置。
示例:
#include<iostream>#include<unistd.h>#include<pthread.h>usingnamespace std;// 子线程的执行函数void*mythread(void*args){// 循环6次,每次打印一次信息,并且每次暂停1秒for(int i =0; i <6;i++){sleep(1);// 休眠1秒钟 cout <<"Child Thread is running ...."<< endl;// 输出子线程运行的提示}// 返回一个值100,并强制转换为 void* 类型return(void*)100;}intmain(){ pthread_t tid;// 定义一个线程ID变量// 创建子线程,传入 mythread 函数作为线程执行的函数,参数为 nullptrpthread_create(&tid,nullptr, mythread,nullptr);// 主线程运行6次,每次输出一次信息,并且每次暂停1秒for(int i =0; i <6;i++){ cout <<"Main Thread is running .."<< endl;// 输出主线程运行的提示sleep(1);// 休眠1秒钟}void* retval;// 声明一个指向 void 的指针,用于接收子线程的返回值// 等待子线程结束,并将子线程的返回值存储到 retval 中pthread_join(tid,&retval);// 输出子线程的返回值,将其强制转换为整数类型并输出 cout <<(int64_t)retval << endl;// 输出子线程的返回值,转换为整数return0;}
程序流程:
  1. 并发输出:主线程和子线程的输出交替进行,打印在终端上时会交替显示 “Main Thread is running …” 和 “Child Thread is running …”。
  2. 同步:主线程通过 pthread_join() 等待子线程完成,并获取子线程的返回值。
  3. 线程返回值:子线程通过 return (void*)100 返回一个 void* 类型的值,主线程通过 pthread_join() 捕获该返回值并输出。

打印:

在这里插入图片描述

pthread_exit

在这里插入图片描述
#include<pthread.h>voidpthread_exit(void*retval);
参数说明
  • retval:这是一个指针,线程退出时可以返回的值。该值可以被其他线程通过 pthread_join 获取,用来传递线程的退出状态或其他信息。
  • 注意:retval 可以是任何类型的指针,通常是一个线程退出的状态信息。
示例:
#include<iostream>#include<unistd.h>#include<pthread.h>usingnamespace std;void*mythread(void* args)// 线程函数{// 子线程执行的操作,循环输出 "Child Thread is running ...."for(int i =0; i <6; i++){sleep(1);// 每次休眠1秒 cout <<"Child Thread is running ...."<< endl;}// 输出结束前的信息 cout <<"Child Pthread_exit"<< endl;// 使用 pthread_exit 显式退出线程并返回一个状态码 100pthread_exit((void*)100);// 这一行代码不会被执行到,因为 pthread_exit 已经退出线程return(void*)100;}intmain(){ pthread_t tid;// 声明一个线程标识符// 创建线程pthread_create(&tid,nullptr, mythread,nullptr);// 主线程执行的操作for(int i =0; i <6; i++){ cout <<"Main Thread is running .."<< endl;sleep(1);// 每次休眠1秒}// 主线程休眠,确保子线程有时间执行sleep(8);// 等待子线程执行完毕// 主线程结束前输出信息 cout <<"main return "<< endl;return0;// 程序结束}
程序流程
  • 程序运行时,子线程和主线程会交替输出 “Child Thread is running …” 和 “Main Thread is running …”。
  • 主线程执行完成它的循环后,调用 sleep(8) 来等待子线程的执行。此时,子线程已经执行完它的循环,并通过 pthread_exit 显式退出。
  • 由于没有在主线程中调用 pthread_join(),主线程并没有等待子线程完成,它只是通过 sleep(8) 暂停一段时间,确保子线程有足够的时间结束。

打印:

在这里插入图片描述

pthread_cancal

在这里插入图片描述
#include<pthread.h>intpthread_cancel(pthread_t thread);
参数说明
  • pthread_t thread:目标线程的线程 ID(TID),即你希望取消的线程。
功能描述
  • pthread_cancel 用于向指定的线程发送取消请求。调用此函数后,目标线程会接收到取消请求,并在合适的时机响应取消请求。
返回值
  • 返回 0 表示成功,其他返回值表示失败,通常是因为无法取消线程(如线程已经结束等)。
示例:
#include<iostream>#include<unistd.h>#include<pthread.h>#include<vector>#include<string>usingnamespace std;// Hex函数,用于将整数转换为十六进制字符串 string Hex(int data){char buff[1034]={0};// snprintf用于将整数data转换为十六进制字符串snprintf(buff,sizeof(buff),"0x%x",data);return buff;}// 线程函数,打印每个线程的整数值void*mythread(void* args){// 将传入的参数指针转换为整型指针int* i =(int*)args;while(true)// 无限循环,模拟线程的持续运行{sleep(1);// 每次休眠1秒 cout <<"thread: "<<*i << endl;// 输出线程ID(即传递给线程的值)}return(void*)100;// 返回一个指针类型的值,通常线程退出时返回状态}intmain(){// 定义一个 vector 来存储线程ID vector<pthread_t> th;// 创建4个线程for(int i =0; i <4; i++){ pthread_t tid;// 定义一个线程ID变量// 创建线程,将线程ID、线程函数(mythread)以及传递给线程的参数(i的地址)传入pthread_create(&tid,nullptr, mythread,&i);// 将线程ID添加到线程容器中 th.push_back(tid);} cout <<"ready calcel"<< endl;sleep(3);// 休眠3秒,等待线程输出// 取消每个创建的线程for(int i =0; i < th.size(); i++){ cout <<"calcel: thread "<< i <<endl;// 输出正在取消的线程编号pthread_cancel(th[i]);// 取消对应的线程}sleep(1);// 稍等1秒,确保线程能够响应取消请求 cout <<"main return "<<endl;// 输出主线程返回信息return0;// 程序结束}
程序流程
  • ** 主线程创建子线程:**主线程使用 pthread_create() 创建 4 个子线程,每个子线程打印传递给它的整数值(即循环中的 i),并在每秒打印一次。
  • **线程输出:**每个线程在无限循环中每秒输出 thread: <value>,其中 <value> 是它收到的整数(传递给线程的 i)。
  • **主线程等待:**主线程在创建完线程后,休眠 3 秒,允许线程输出信息。
  • **取消线程:**主线程在 3 秒后调用 pthread_cancel() 来取消所有 4 个线程,每个线程被取消后,它会终止执行(线程的退出取决于它们在什么地方被取消)。
  • **程序结束:**主线程输出 "main return" 并结束,程序执行完毕。

打印:

在这里插入图片描述

线程分离

在这里插入图片描述
#include<pthread.h>intpthread_detach(pthread_t thread);

参数

  • thread:要设置为分离状态的线程 ID。

功能描述

  • 功能描述pthread_detach 用于将一个线程设置为 分离状态。当线程被设置为分离状态后,线程在完成执行时会自动释放资源,而不需要其他线程显式地调用 pthread_join() 来清理线程资源。

示例:

#include<iostream>#include<unistd.h>#include<pthread.h>#include<vector>#include<string>usingnamespace std;// Hex函数:将整数转换为十六进制字符串 string Hex(int data){char buff[1034]={0};snprintf(buff,sizeof(buff),"0x%x", data);// 将整数以十六进制格式转换成字符串return buff;}// 线程函数void*mythread(void* args){pthread_detach(pthread_self());// 将当前线程设置为分离状态int* i =(int*)args;// 将传入的参数指针转换为整型指针int n =3;// 循环打印线程编号(传递给线程的值),共打印3次while(n--){sleep(1);// 每次循环休眠1秒 cout <<"thread: "<<*i << endl;// 输出线程的编号}return(void*)100;// 线程返回值}intmain(){ vector<pthread_t> th;// 用于存储线程ID的容器 cout <<"线程的创建"<< endl;// 创建4个线程for(int i =0; i <4; i++){ pthread_t tid;pthread_create(&tid,nullptr, mythread,&i);// 创建线程并传递 i 的地址作为参数 th.push_back(tid);// 将创建的线程ID加入到线程容器中}sleep(10);// 主线程休眠10秒,确保所有子线程能运行完 cout <<"main return "<< endl;return0;// 主程序结束}

程序执行流程:

  1. 主线程通过 pthread_create 创建 4 个子线程,每个子线程都会执行 mythread 函数。
  2. 每个子线程会打印 3 次其编号(线程的参数),并在每次打印后休眠 1 秒。
  3. 主线程休眠 10 秒,以确保子线程有足够的时间打印信息。
  4. 每个子线程在完成执行后会自动退出并释放资源,因为它们是分离线程(调用了 pthread_detach)。
  5. 主线程输出 “main return” 并结束。

打印:

在这里插入图片描述
  • 以上的代码没有实现同步,可能会导致错乱打印。

Read more

OpenClaw 安装 + 接入飞书机器人完整教程

OpenClaw 安装 + 接入飞书机器人完整教程 OpenClaw 曾用名:ClawdBot → MoltBot → OpenClaw(同一软件,勿混淆) 适用系统:Windows 10/11 最后更新:2026年3月 一、什么是 OpenClaw? OpenClaw 是一款 2026 年爆火的开源个人 AI 助手,GitHub 星标已超过 10 万颗。 与普通 AI 聊天机器人的核心区别: * 真正的执行能力:不只回答问题,能实际操作你的电脑 * 24/7 全天候待命:睡觉时也能主动完成任务 * 完全开源免费:数据完全掌控在自己手中 * 支持国内平台:飞书、钉钉等均已支持接入 二、安装前准备:安装 Node.js 建议提前手动安装

By Ne0inhk
共绩算力 RTX 5090 极速部署 Stable Diffusion WebUI:新手也能秒开 AI 绘图工作站

共绩算力 RTX 5090 极速部署 Stable Diffusion WebUI:新手也能秒开 AI 绘图工作站

还在为本地硬件不足跑不动 AI 绘图模型发愁?想快速拥有高性价比的 Stable Diffusion 绘图环境?今天给大家带来共绩算力 RTX 5090 部署 Stable Diffusion WebUI(增强版)的详细教程,全程零兼容冲突,从云主机配置到生成第一张 AI 画作仅需 30 分钟,步骤清晰可复现,无论是设计爱好者还是 AI 新手都能轻松上手! 目录 一、为什么选择共绩算力部署 Stable Diffusion? 二、环境准备:精准配置云主机 2.1 创建云主机实例 1.2 登录云主机终端 二、完整部署流程 2.1 环境清理与依赖安装 2.2 下载与配置Stable Diffusion WebUI

By Ne0inhk
openclaw 对接完飞书群机器人配置踩坑记:消息不回、Gateway 断开问题排查

openclaw 对接完飞书群机器人配置踩坑记:消息不回、Gateway 断开问题排查

前言 用 OpenClaw 配飞书机器人,踩了两个坑:群消息不回、Gateway 总是断开。排查了好一阵子,总算搞定了,记录一下希望能帮到遇到同样问题的朋友。 发现问题 飞书消息不回复 在飞书群里 @ 了机器人,完全没反应。一开始以为是网络不好或者机器人没上线,但状态显示明明是连接着的,这就奇怪了。 Gateway 频繁断开 每次改完配置跑 openclaw gateway restart,或者根本什么都没干,Gateway 说断就断。再想启动就报错,必须跑一遍 openclaw doctor --fix 重新安装才能用。太影响使用了。 查看原因 飞书机器人 ID 搞错了 翻日志看到这么一句: receive events or callbacks through persistent connection only available in

By Ne0inhk
【VR音游】音符轨道系统开发实录与原理解析(OpenXR手势交互)

【VR音游】音符轨道系统开发实录与原理解析(OpenXR手势交互)

VR音游音符轨道系统开发实录与原理解析 在 VR 音游的开发过程中,音符轨道系统是最核心的交互与可视化部分。本文结合一次完整的开发实录,分享从核心原理与设计到VR内容构建的完整过程,帮助读者快速理解音符轨道系统的实现思路。 文章目录 * VR音游音符轨道系统开发实录与原理解析 * 一、实录结果 * 二、VR内容开发步骤 * 1. 准备音符与交互逻辑 * 2. 创建谱面 * 3. 绘制音轨 * 4. 预制件与音频替换 * 三、原理解析(音符轨道系统) * 1. 音符轨道(Note Track) * 2. 轨迹调节与偏移控制 * 3. 音符触摸激活 * 4. 谱面编辑工具(Editor 功能) * 四、总结与展望 * 1. 成果回顾:从零到一的核心突破 * 2. 技术总结:核心设计理念 * 3. 开发难点与问题反思 * 4. 优化策略与改进方向 * 5.

By Ne0inhk