深入理解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>
int pthread_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>    using namespace std;    // 线程函数
void* mythread(void* args)
{// 创建一个循环,子线程会打印 6 次for(int i = 0; i < 6; i++) {sleep(1);                // 让线程睡眠 1 秒钟,模拟任务的执行cout << "Child Thread" << endl;  // 打印“Child Thread”,表示子线程在运行}return nullptr;
}// 主函数
int main()
{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 线程执行完毕return 0;  // 程序正常退出
}
  • pthread_create():用于创建一个新的线程,mythread 是新线程的执行函数。该函数接受参数 nullptr,表示没有传递任何参数给线程。
  • pthread_join(tid, nullptr):等待线程 tid 执行完成,pthread_join 阻塞主线程,直到子线程执行完毕。

打印结果:

在这里插入图片描述

线程退出

pthread_join

在这里插入图片描述

#include <pthread.h>
int pthread_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>using namespace 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;
}int main()
{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;  // 输出子线程的返回值,转换为整数return 0;
}
程序流程:
  1. 并发输出:主线程和子线程的输出交替进行,打印在终端上时会交替显示 “Main Thread is running …” 和 “Child Thread is running …”。
  2. 同步:主线程通过 pthread_join() 等待子线程完成,并获取子线程的返回值。
  3. 线程返回值:子线程通过 return (void*)100 返回一个 void* 类型的值,主线程通过 pthread_join() 捕获该返回值并输出。

打印:

在这里插入图片描述

pthread_exit

在这里插入图片描述

#include <pthread.h>
void pthread_exit(void *retval);
参数说明
  • retval:这是一个指针,线程退出时可以返回的值。该值可以被其他线程通过 pthread_join 获取,用来传递线程的退出状态或其他信息。
  • 注意:retval 可以是任何类型的指针,通常是一个线程退出的状态信息。
示例:
#include<iostream>
#include<unistd.h>
#include<pthread.h>using namespace 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;
}int main() 
{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;return 0;  // 程序结束
}
程序流程
  • 程序运行时,子线程和主线程会交替输出 “Child Thread is running …” 和 “Main Thread is running …”。
  • 主线程执行完成它的循环后,调用 sleep(8) 来等待子线程的执行。此时,子线程已经执行完它的循环,并通过 pthread_exit 显式退出。
  • 由于没有在主线程中调用 pthread_join(),主线程并没有等待子线程完成,它只是通过 sleep(8) 暂停一段时间,确保子线程有足够的时间结束。

打印:

在这里插入图片描述

pthread_cancal

在这里插入图片描述

#include <pthread.h>
int pthread_cancel(pthread_t thread);
参数说明
  • pthread_t thread:目标线程的线程 ID(TID),即你希望取消的线程。
功能描述
  • pthread_cancel 用于向指定的线程发送取消请求。调用此函数后,目标线程会接收到取消请求,并在合适的时机响应取消请求。
返回值
  • 返回 0 表示成功,其他返回值表示失败,通常是因为无法取消线程(如线程已经结束等)。
示例:
#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<vector>
#include<string>
using namespace 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;  // 返回一个指针类型的值,通常线程退出时返回状态
}int main()
{// 定义一个 vector 来存储线程IDvector<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;  // 输出主线程返回信息return 0;  // 程序结束
}
程序流程
  • ** 主线程创建子线程:**主线程使用 pthread_create() 创建 4 个子线程,每个子线程打印传递给它的整数值(即循环中的 i),并在每秒打印一次。

  • **线程输出:**每个线程在无限循环中每秒输出 thread: <value>,其中 <value> 是它收到的整数(传递给线程的 i)。

  • **主线程等待:**主线程在创建完线程后,休眠 3 秒,允许线程输出信息。

  • **取消线程:**主线程在 3 秒后调用 pthread_cancel() 来取消所有 4 个线程,每个线程被取消后,它会终止执行(线程的退出取决于它们在什么地方被取消)。

  • **程序结束:**主线程输出 "main return" 并结束,程序执行完毕。

打印:

在这里插入图片描述

线程分离

在这里插入图片描述

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

参数

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

功能描述

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

示例:

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<vector>
#include<string>
using namespace 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;  // 线程返回值
}int main()
{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;return 0;  // 主程序结束
}

程序执行流程:

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

打印:

在这里插入图片描述

  • 以上的代码没有实现同步,可能会导致错乱打印。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/902169.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(mac)Grafana监控系统之监控Linux的Redis

Grafana安装-CSDN博客 普罗米修斯Prometheus监控安装&#xff08;mac&#xff09;-CSDN博客 1.Redis_exporter安装 直接下载 wget https://github.com/oliver006/redis_exporter/releases/download/v1.0.3/redis_exporter-v1.0.3.linux-amd64.tar.gz 解压 tar -xvf redis_…

鸿蒙应用元服务开发-Account Kit未成年人模式订阅和处理用户信息变更

一、概述 通过订阅用户信息变更&#xff0c;您可以接收有关用户及其账户的重要更新。当用户取消元服务的授权信息、注销华为账号时&#xff0c;华为账号服务器会发送通知到元服务&#xff0c;元服务可以根据通知消息进行自身业务处理。 二、用户信息变更事件介绍 三、订阅用…

buildroot构建根文件系统报错(已解决大部分问题)

title: buildroot构建根文件系统报错(set FORCE_UNSAFE_CONFIGURE1) author: cbus categories: 小知识 tags:小知识 abbrlink: 53691 date: 2025-04-20 08:03:00 错误1 set FORCE_UNSAFE_CONFIGURE1 在使用buildroot构建根文件系统时&#xff0c;一切按照文档的配置&#xff0…

7.QT-常用控件-QWidget|font|toolTip|focusPolicy|styleSheet(C++)

font API说明font()获取当前widget的字体信息.返回QFont对象.setFont(const QFont& font)设置当前widget的字体信息. 属性说明family字体家族.⽐如"楷体",“宋体”,"微软雅⿊"等.pointSize字体⼤⼩weight字体粗细.以数值⽅式表⽰粗细程度取值范围为[…

通过面向目标的奖励弥合人与机器人的灵活性差距

24年10月来自纽约大学的论文“Bridging the Human to Robot Dexterity Gap through Object-Oriented Rewards”。 直接通过人类视频训练机器人是机器人技术和计算机视觉领域的一个新兴领域。尽管双指机械手在双指夹持器方面取得了显著进展&#xff0c;但以这种方式让多指机械手…

C++入门篇(下)

目录 1、引用 1.1 引用概念 1.2 引用特性 1.3 常引用 1.4 使用场景 1.4.1 引用做参数 1.4.2 引用做返回值 1.5 引用和指针的区别 2、内联函数 2.1 概念 2.2 特性 3、auto关键字 4、基于范围的for循环 5、指针空值nullptr 5.1 C98 中的指针空值处理 5.2 C11 …

Multi-Query Attention (MQA) PyTorch 实现

和多头注意力机制的唯一区别&#xff1a;K、V在不同的head之间实现了复用&#xff0c;而对于不同的头&#xff0c;Q依然不同。 因此这里的代码和标准多头注意力的实现也是几乎完全一样&#xff1a; import torch import torch.nn as nn import torch.nn.functional as Fclass…

visual studio无法跳转到函数定义、变量定义、跳转函数位置不准问题解决

参考&#xff1a;https://blog.csdn.net/snakehacker/article/details/135438353 程序有时会出现大部分函数都不能准确的从头文件中正确定位到函数定位,这是因为数据库错乱造成的,可以通过重构数据库来解决,操作方法如下&#xff1a; 菜单栏&#xff1a;工具——选项 文本编辑…

Java优雅实现判空方法

在 Java 开发中&#xff0c;频繁的 if (obj ! null) 判空代码会导致代码冗余、可读性差&#xff0c;且容易遗漏判空导致 NullPointerException。以下从 语言特性、设计模式、工具类 和 编码规范 四个维度&#xff0c;结合实际案例&#xff0c;详解如何优雅处理空值问题。 一、…

京东百亿补贴杀入外卖市场:一场关乎即时零售未来的攻防战

当美团和饿了么在外卖市场双雄争霸十余年之际&#xff0c;京东突然以"百亿补贴免佣金"的组合拳高调入场。这场看似跨界的外卖大战&#xff0c;实则是互联网巨头对万亿级即时零售市场的生死争夺。 外卖只是表象&#xff0c;即时零售才是终极战场 京东黑板报4月10日官…

UNION和UNION ALL的主要区别

UNION和UNION ALL的主要区别在于处理重复数据和排序的方式。 UNION和UNION ALL都是SQL语言中用于合并两个或多个SELECT语句结果集的关键字。它们的主要区别如下&#xff1a; 1、对重复结果的处理&#xff1a;UNION在进行表链接后会筛选掉重复的记录&#xff0c;而UNION ALL不会…

七段码 路径压缩 并查集 dfs

12.七段码 - 蓝桥云课 将七个二极管映射为 1-7 开一个二维矩阵 为 相邻的边连上线 edge[1][2] edge[1][6] 1;edge[2][1] edge[2][3] edge[2][7] 1;edge[3][2] edge[3][4] edge[3][7] 1;edge[4][3] edge[4][5] 1;edge[5][4] edge[5][6] edge[5][7] 1;edge[6][1…

科技如何改变世界?

技术是我们日常生活中不可或缺的一部分&#xff0c;以至于我们常常忘记了它的重要性。如果你正在科技领域工作&#xff0c;或者希望进入该领域&#xff0c;你可能是众多有使命感的人之一&#xff0c;希望知道自己的日常工作能为社会或地球的长远利益做出贡献。 别再四处寻找了…

抽象的https原理简介

前言 小明和小美是一对好朋友&#xff0c;他们分隔两地&#xff0c;平时经常写信沟通&#xff0c;但是偶然被小明发现他回给小美的信好像被人拆开看过&#xff0c;甚至偷偷被篡改过。 对称加密算法 开头的通信过程比较像HTTP服务器与客户端的通信过程&#xff0c;全明文传输…

高级java每日一道面试题-2025年4月13日-微服务篇[Nacos篇]-Nacos如何处理网络分区情况下的服务可用性问题?

如果有遗漏,评论区告诉我进行补充 面试官: Nacos如何处理网络分区情况下的服务可用性问题&#xff1f; 我回答: 在讨论 Nacos 如何处理网络分区情况下的服务可用性问题时&#xff0c;我们需要深入理解 CAP 理论以及 Nacos 在这方面的设计选择。Nacos 允许用户根据具体的应用…

python解压文件 zip tar.gz tar.xz

以下代码为解压zip包 tar包文件 zip_path&#xff1a;文件绝对路径 output_folder&#xff1a;文件解压后存放的文件夹路径 def extract_file(zip_path, output_folder):# 支持解压zip tar tar.gz tar.xz .tar.bz2# 确保输出文件夹存在os.makedirs(output_folder, exist_okT…

网络基础(协议,地址,OSI模型、Socket编程......)

目录 一、计算机网络发展 二、协议 1.认识协议 2.OSI七层模型 3.TCP/IP 五层(或四层)模型 4.协议本质 三、网络传输流程 1.MAC地址 2.协议栈 3.IP地址 IP地址 vs MAC地址 1. 核心区别 2. 具体通信过程类比 3. 关键总结 为什么需要两者&#xff1f; 4.协议栈图解…

生成式AI对话中提示词策略:明确问题、明确目标和提供背景信息是最有效的策略

生成式AI对话中提示词策略:明确问题、明确目标和提供背景信息是最有效的策略 最有效的提示词策略包括明确问题、明确目标和提供背景信息。普适性有效提示词策略可分为三类:明确需求与精确指引型、清晰解释与逻辑排序型、拆解任务与多样化表达型。[局限]数据来源于中国用户,…

AtCoder ABC402 ABCD

A - CBC 把大写字母按顺序连起来 B - Restaurant Queue 一眼队列&#xff0c;stl模拟就行 C - Dislike Foods 显然&#xff0c;每次克服暴力枚举每个菜肴会超时。 然而题目中给了每个菜肴的配菜个数&#xff0c;不妨换过来统计每个配菜用在了哪些菜肴。每次克服时&#x…

Transformer 架构 - 解码器 (Transformer Architecture - Decoder)

欢迎回到我们的 Transformer 系列教程!在上一篇中,我们详细探讨了 Transformer 的编码器,它负责将输入的源序列(比如源语言句子)转换为一系列包含丰富上下文信息的向量表示。 现在,我们将把目光投向 Transformer 的另一半——解码器 (Decoder)。解码器负责接收编码器的输…