[Linux]多线程编程

[Linux]多线程编程

文章目录

  • [Linux]多线程编程
    • pthread_create函数
    • pthread_join函数
    • pthread_exit函数
    • pthread_cancel函数
    • pthread_self函数
    • pthread_detach函数
    • 理解线程库和线程id

Linux操作系统下,并没有真正意义上的线程,而是由进程中的轻量级进程(LWP)模拟的线程,因此Linux操作系统中只会提供进程操作的系统接口。但是为了用户操作方便,Linux操作系统提供了用户级的原生线程库,原生线程库将系统接口进行封装,让用户可以像使用操作真正的线程一样进行线程操作,另外由于使用的是原生线程库,编译代码时需要指明线程库进行链接。

pthread_create函数

pthread_create函数用于创建线程。

//pthread_create函数所在的头文件和函数声明
#include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
  • **thread参数:**返回线程ID

  • **attr参数:**设置线程的属性,attr为NULL表示使用默认属性

  • start_routine参数:是个函数地址,线程启动后要执行的函数

  • **arg参数:**传给线程启动函数的参数

  • **返回值:**成功返回0,失败返回错误码。(由于线程共用同一个地址空间,因此不采用设置全局变量errno的方式记录错误码)

编写如下代码进行测试:

#include <iostream>
#include <unistd.h>
#include <pthread.h>using namespace std;void *thread_run(void *args)
{while(true){cout << "new pthread running" << endl;sleep(1);}return nullptr;
}int main()
{pthread_t t;int n = pthread_create(&t, nullptr, thread_run, nullptr);if (n!=0) cerr << "new thread error" << endl; while(true){cout << "main pthread running, new pthread id: " << t << endl;sleep(1);}return 0;
}

编译代码并运行查看结果:

pthreadTest1

另外,可以使用Linux操作系统的指令ps -aL | head -1 && ps -aL | grep 进程名查看线程:

pthreadTest2

其中LWPid和pid相同的是主线程,其余的是新线程。

pthread_join函数

和进程类似,Linux操作系统下线程退出后,也要进行线程等待回收新线程,因此提供了pthread_join函数。

//pthread_join函数所在的头文件和函数声明#include <pthread.h>int pthread_join(pthread_t thread, void **retval);
  • thread参数: 要等待并回收的新线程id。(pthread_create函数创建新线程时的输出型参数thread)
  • retval参数: 作为输出型参数接收线程退出的返回值。
  • **返回值:**成功返回0,失败返回错误码。
  • 如果线程被取消了,retval参数会接收到PTHREAD_CANCELED ((void *) -1)

注意: 由于线程异常会产生信号直接导致进程终止,因此线程等待回收时不需要考虑异常情况检测。

编写如下代码进行测试:

#include <iostream>
#include <unistd.h>
#include <pthread.h>#define NUM 10using namespace std;void *thread_run(void *args)
{while(true){cout << "new pthread running" << endl;sleep(4);break;}return (void*)0;//返回值为0的数据
}int main()
{pthread_t tid[NUM];for (int i = 0; i < NUM; i++){int n = pthread_create(tid+i, nullptr, thread_run, nullptr);if (n!=0) cerr << "new thread error, thread-" << i << endl; }void *ret = nullptr;for (int i = 0; i < NUM; i++){int m = pthread_join(tid[i], &ret);//等待回收新线程if (m!=0) cerr << "new thread join error, thread-" << i << endl; cout << "thread-" << i << "quit, ret: " << (uint64_t)ret << endl;//打印新线程退出返回值}cout << "all thread quit" << endl;//打印说明所有线程退出return 0;
}

编译代码并运行查看结果,在进程执行时采用指令while :; do ps -aL | head -1 && ps -aL | grep 进程名; sleep 1; done检测进程:

pthreadTest3

可以看到线程退出return返回了返回值为0的数据,主线程调用pthread_join能够成功接收返回值。

pthread_exit函数

Linux操作系统下线程退出的方式:

  1. 线程执行函数结束,return返回
  2. 调用phread_exit函数退出线程

注意: 无论是线程执行函数结束,return返回还是调用phread_exit函数退出线程,最终都会给主线程返回一个void *类型的返回值。

//pthread_exit函数所在的头文件和函数声明
#include <pthread.h>void pthread_exit(void *retval);
  • retval参数: 作为线程终止的返回值返回给主线程。

编写如下代码进行测试:

#include <iostream>
#include <unistd.h>
#include <pthread.h>#define NUM 10using namespace std;void *thread_run(void *args)
{while(true){cout << "new pthread running" << endl;sleep(4);pthread_exit((void*)1);}
}int main()
{pthread_t tid[NUM];for (int i = 0; i < NUM; i++){int n = pthread_create(tid+i, nullptr, thread_run, nullptr);if (n!=0) cerr << "new thread error, thread-" << i << endl; }void *ret = nullptr;for (int i = 0; i < NUM; i++){int m = pthread_join(tid[i], &ret);//等待回收新线程if (m!=0) cerr << "new thread join error, thread-" << i << endl; cout << "thread-" << i << "quit, ret: " << (uint64_t)ret << endl;//打印新线程退出返回值}cout << "all thread quit" << endl;//打印说明所有线程退出return 0;
}

编译代码并运行查看结果:

pthreadTest4

可以看到线程退出phread_exit返回了值为1的数据,主线程调用pthread_join能够成功接收返回值。

pthread_cancel函数

pthread_cancel函数能够将正在运行的线程取消。

//pthread_cancel函数所在的头文件和函数声明
#include <pthread.h>int pthread_cancel(pthread_t thread);
  • thread参数: 要取消的线程id。
  • **返回值:**成功返回0,失败返回错误码。

编写如下代码进行测试:

#include <iostream>
#include <unistd.h>
#include <pthread.h>using namespace std;void *thread_run(void *args)
{while (true)//新线程死循环执行代码{cout << "new thread running" << endl;sleep(1);}pthread_exit((void *)11);
}int main()
{pthread_t t;pthread_create(&t, nullptr, thread_run, nullptr);int cnt = 3;while (true){sleep(1);if ((cnt--) == 0){pthread_cancel(t);//取消新线程break;}}sleep(2);void *ret = nullptr;pthread_join(t, &ret);//等待回收新线程cout << "new thread quit " << "ret: " << (int64_t)ret << endl;return 0;
}

编译代码并运行查看结果:

pthreadTest5

死循环的新线程被主线程取消了,新线程被取消后,主线程pthread_join函数接收到的是PTHREAD_CANCELED ((void *) -1)

pthread_self函数

pthread_self函数用于获取当前线程的线程id。

//pthread_self函数所在的头文件和函数声明
#include <pthread.h>pthread_t pthread_self(void);
  • 返回值: 返回调用线程的线程id。

编写如下代码进行测试:

#include <iostream>
#include <pthread.h>using namespace std;void *thread_run(void *args)
{pthread_t tid = pthread_self();cout << "i am new thread, my thread id: " << tid << endl;return nullptr;
}int main()
{pthread_t t;pthread_create(&t, nullptr, thread_run, nullptr);pthread_join(t, nullptr);cout << "new thread id: " << t << endl;return 0;
}

编译代码并运行查看结果:

image-20230923174750463

pthread_detach函数

默认情况下,新创建的线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。如果我们将线程分离,当线程退出时,自动释放线程资源。Linux操作系统下提供了pthread_detach函数用于分离线程。

//pthread_detach函数所在的头文件和函数声明
#include <pthread.h>int pthread_detach(pthread_t thread);
  • thread参数: 要分离的线程id。
  • 线程分离后无法进行pthread_join操作,如果使用了就会报错。

先编写如下代码进行测试:

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <cstring>using namespace std;void *thread_run(void *args)
{int cnt = 5;while(true){cout << (char*)args << " : " << cnt-- << endl;if (cnt==0) break;}return nullptr;
}int main()
{pthread_t t;pthread_create(&t, nullptr, thread_run, (void*)"new_thread");pthread_detach(t);//分离线程int n = pthread_join(t, nullptr);//线程等待if (n != 0){cerr << "pthread_join error: " << n << " : " << strerror(n) << endl; }return 0;
}

编译代码并运行查看结果:

image-20230923193701650

由于主线程和新线程的调度问题,造成了如上两种情况,但是无论哪种情况,新线程分离后,在进行等待操作就会报错。

再编写如下代码进行测试:

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <cstring>using namespace std;void *thread_run(void *args)
{pthread_detach(pthread_self());//线程分离int cnt = 5;while(true){cout << (char*)args << " : " << cnt-- << endl;sleep(1);if (cnt==0) break;}return nullptr;
}int main()
{pthread_t t;pthread_create(&t, nullptr, thread_run, (void*)"new_thread");int n = pthread_join(t, nullptr);//线程等待if (n != 0){cerr << "pthread_join error: " << n << " : " << strerror(n) << endl; }return 0;
}

编译代码并运行查看结果:

pthreadTest6

主线程先进行等待,新线程后进行分离,就会造成如上这种即使线程分离了等待操作也未报错。因为在主线程进行等待操作时检测新线程未分离,就直接进入阻塞等待新线程的状态了,因此不会报错。

理解线程库和线程id

Linux操作系统下,没有真正的线程,而是用轻量级进程模拟的线程,因此Linux操作系统只能以轻量级进程的方式进行管理,而不能以线程的方式管理,而线程库要给用户提供线程相关的各种各样的操作,线程库就要承担一部分操作系统不具有的线程管理操作,因此用于调用线程库创建线程时,线程库就要创建对应的数据结构记录线程的属性以用于管理线程,在后续调用线程库操作线程时,线程库就会利用之前创建的记录属性的结构和封装的一些系统接口来实现线程操作。

image-20230923203756664

线程库在组织线程管理结构时,会将其线性的记录在进程地址空间上,而线程管理结构在进程地址空间上的首地址就是线程库提供的线程id。

image-20230923204156954

在线程库提供的线程管理结构中,存在一部分空间称为线程栈,线程栈是每个新线程私有的栈,新线程会将创建的临时变量存在线程栈中,将每个线程的数据分离开,以便进行数据的管理,而主线程使用的是进程地址空间中的栈结构。

说明: 在Linux操作系统下,C++提供的线程操作都是对原生线程库的封装。

编写如下代码进行测试:

#include <iostream>
#include <unistd.h>
#include <thread>using namespace std;void run1()
{while(true){cout << "thread 1" << endl;sleep(1);}
}
void run2()
{while(true){cout << "thread 2" << endl;sleep(1);}
}
void run3()
{while(true){cout << "thread 3" << endl;sleep(1);}
}int main()
{thread th1(run1);thread th2(run2);thread th3(run3);th1.join();th2.join();th3.join();return 0;
}

在编译时不加-lpthread选项并运行程序结果如下:

image-20230923205821115

由于C++的线程操作是封装原生线程库得来的,如果编译时不链接原生线程库,在程序执行时,操作系统会无法正确的加载动态库到内存中,会导致程序无法正常执行。

补充: 线程管理结构中线程局部存储用于存储线程相关的全局变量、线程上下文信息、隔离敏感数据。

image-20230927102412819

编写如下代码进行测试:

#include <iostream>
#include <unistd.h>
#include <pthread.h>using namespace std;__thread int g_val = 100;//__thread将数据以线程全局变量的形式创建void *thread_run(void *args)
{char* tname = static_cast<char*>(args);int cnt = 5;while (true){cout << tname << ":" << (u_int64_t)cnt << ",g_val: " << g_val << ",&g_val: " << &g_val << endl;if ((cnt--)==0)break;sleep(1);}return nullptr;
}int main()
{pthread_t tid1;pthread_t tid2;pthread_t tid3;pthread_create(&tid1, nullptr, thread_run, (void*)"thread1");pthread_create(&tid2, nullptr, thread_run, (void*)"thread2");pthread_create(&tid3, nullptr, thread_run, (void*)"thread3");pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);pthread_join(tid3, nullptr);return 0;
}

编译代码并运行查看结果:

image-20230927102959242

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

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

相关文章

在多台服务器上运行相同命令(二)、clush

介绍安装配置互信认证参数含义基本使用节点组拷贝文件 介绍 Clush&#xff08;Cluster Shell&#xff09;是一个用于管理和执行集群操作的工具&#xff0c;它允许你在多台远程主机上同时执行命令&#xff0c;以便批量管理服务器。Clush 提供了一种简单而强大的方式来管理大规模…

“押宝高手”乐视视频再出手,看中商业传奇剧《大盛魁》

作为最早开始版权采购的长视频平台&#xff0c;乐视视频一向擅长“押宝”优质内容。从《甄嬛传》到《白鹿原》等&#xff0c;乐视拿下了众多经典古装剧、年代剧的版权。 9月&#xff0c;乐视视频再次出手拿下的历史传奇剧《大盛魁》开始热播。该剧由王新民导演执导&#xff0c…

全渠道客服体验:Rocket.Chat 的无缝互动 | 开源日报 No.41

RocketChat/Rocket.Chat Stars: 36.9k License: NOASSERTION Rocket.Chat 是一个完全可定制的开源通信平台&#xff0c;适用于具有高标准数据保护要求的组织。我们是团队沟通场景下的最终免费开源解决方案&#xff0c;可以实现同事之间、公司之间或客户之间的实时对话。提高生…

SSM - Springboot - MyBatis-Plus 全栈体系(十三)

第三章 MyBatis 一、MyBatis 简介 1. 简介 MyBatis 最初是 Apache 的一个开源项目 iBatis, 2010 年 6 月这个项目由 Apache Software Foundation 迁移到了 Google Code。随着开发团队转投 Google Code 旗下&#xff0c; iBatis3.x 正式更名为 MyBatis。代码于 2013 年 11 月迁…

TS中class类的基本使用

想要创建对象&#xff0c;必须要先定义类&#xff0c;所谓的类可以理解为对象的模型&#xff0c;程序中可以根据类创建所指定类型的对象。 一、使用class关键字定义类 class 类名 { } // 使用class关键字来定义一个类 class Person{}// 使用new关键字创建一个对象 const per …

Pikachu靶场——SSRF 服务端请求伪造

文章目录 1 SSRF 服务端请求伪造1.1 SSRF(curl)1.1.1 漏洞防御 1.2 SSRF(file_get_content)1.2.1 漏洞防御1.2.3 SSRF 防御 1 SSRF 服务端请求伪造 SSRF(Server-Side Request Forgery:服务器端请求伪造) 其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能&a…

【红外与可见光图像融合】离散平稳小波变换域中基于离散余弦变换和局部空间频率的红外与视觉图像融合方法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Qwen-VL:多功能视觉语言模型,能理解、能定位、能阅读等

Overview 总览摘要1 引言2 方法2.1 模型结构2.2 输入输出 3 训练3.1 预训练3.2 多任务预训练3.3 监督finetune 4 评测4.1 图像文本描述和视觉问答4.2 面向文本的视觉问答4.3 指代表达理解4.4 视觉语言任务中的小样本学习4.4 现实用户行为下的指令遵循 5 相关工作6 总结与展望附…

【boost网络库从青铜到王者】第七篇:asio网络编程中的异步echo服务器,以应答为主

文章目录 1、简介2、echo模式应答异步服务器2.1、Session会话类2.2、Server类为服务器接收连接的管理类 3、客户端4、隐患5、总结 1、简介 前文已经介绍了异步操作的api&#xff0c;今天写一个简单的异步echo服务器&#xff0c;以应答为主。 2、echo模式应答异步服务器 2.1、…

小样本学习

一、基础知识 小样本学习&#xff08;few shot learning&#xff09;旨在使用先验知识&#xff08;prior knowledge&#xff09;基于有限数量的样本推广&#xff08;generaling&#xff09;到新任务&#xff08;new task&#xff09;。这些先验&#xff08;prior knowledge&am…

初识C语言——详细入门(系统性学习day4)

目录 前言 一、C语言简单介绍、特点、基本构成 简单介绍&#xff1a; 特点&#xff1a; 基本构成&#xff1a; 二、认识C语言程序 标准格式&#xff1a; 简单C程序&#xff1a; 三、基本构成分类详细介绍 &#xff08;1&#xff09;关键字 &#xff08;2&#xf…

Blender导出FBX给UE5

最近在学习UE5的资源导入&#xff0c;总结如下&#xff1a; 建模使用Blender&#xff0c;UE5版本是5.3 1.纯静态模型导入UE5 Blender FBX导出设置保持默认即可&#xff0c; UE5把导入设置里Miscellaneous下Force Front XAxis和Convert Scene Unit勾选即可 2.带骨骼动画的模型…

字符函数和字符串函数模拟实现与详解————长度不受限制的字符串函数

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂。 目录 1.前言 2strlen函数 3.strcpy函数…

csdn未经允许将我的文章设置成vip收费

以前在csdn写了一些笔记&#xff0c;后来不用csdn了&#xff0c;想着留下这些笔记或多或少能帮助其他初学者&#xff0c;就没管它。结果csdn把文章设置成收费了&#xff0c;这个收费不是我本人弄的&#xff0c;是csdn弄的&#xff01;我现在只能把这些文章删除掉了。

Docker初识

什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署&#xff0c;环境不一定一致&#xff0c;会遇到…

Tungsten Fabric数据量过大问题处理初探

开源SDN系统Tungsten Fabric面临数据产生过多问题。 经排查&#xff0c;产生数据多出自analytics组件的Cassandra数据库()。很多分析数据会存储至Cassandra库&#xff0c;并持久化处理。 没有特殊调整的话&#xff0c;目录在 /var/lib/docker/volumes/analytics_database_an…

【大数据开发技术】实验04-HDFS文件创建与写入

文章目录 一、实验目标二、实验要求三、实验内容四、实验步骤 一、实验目标 熟练掌握hadoop操作指令及HDFS命令行接口掌握HDFS原理熟练掌握HDFS的API使用方法掌握单个本地文件写入到HDFS文件的方法掌握多个本地文件批量写入到HDFS文件的方法 二、实验要求 给出主要实验步骤成…

【新版】系统架构设计师 - 案例分析 - 架构设计<SOA与微服务>

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 案例分析 - 架构设计&#xff1c;SOA与微服务&#xff1e;例题1例题2例题3例题4 架构 - 案例分析 - 架构设计&#xff1c;SOA与微服务&#xff1e; 这里SOA与微服务的例题只对应找寻了几个&#x…

一款强大的ntfs磁盘读写工具Paragon NTFS 15破解版百度网盘下载

今天再给大家分享一款NTFS工具Paragon NTFS 15&#xff0c;Paragon NTFS 15破解版是目前的最新版&#xff0c;需要的赶快收藏&#xff0c;地址失效可以留言。 Paragon Ntfs For Mac 15下载&#xff1a;https://souurl.cn/s84CCB Crcak链接: https://pan.baidu.com/s/1c2Hx7QBE…

计算机网络工程师多选题系列——计算机网络

2 计算机网络 2.1 网络技术基础 题型1 TCP/IP与ISO模型的问题 TCP/IP由IETF制定&#xff0c;ISO由OSI制定&#xff1b; TCP/IP分为四层&#xff0c;分别是主机-网络层、互联网络层、传输层和应用层&#xff1b;OSI分为七层&#xff0c;分别是物理层、数据链路层、网络层(实…