21.Linux 线程库的使用与封装

在linux内核中并没有线程的概念,只有轻量级进程LWP的概念,linux下的线程都是是由LWP进行模拟实现的。因此linux操作系统中不会提供线程的相关接口,只会提供轻量级线程的接口(如vfork,clone等)。但是在我们的学习过程中实际上学到的都是线程的概念,故而linux的设计者在用户和操作系统之间将轻量级进程的的系统调用进行了封装成库并提供给了用户,这样的线程我们称之为用户级线程库

接下来我们介绍一下linux系统中线程库的使用与封装。

一、线程库的使用

1.1 POSIX线程库

在linux中,与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以pthread_ 打头的。要想使用这些函数库,要通过引入头文件 <pthread.h>,而在链接这些线程函数库时要使用编译器命令的-lpthread选项。

1.2 线程库的接口

1.2.1 线程创建

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,失败返回错误码

示例:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;void* run(void* arg)
{while(true){cout<<"new thread :"<<(char* )arg<<endl;sleep(1);}
}int main()
{pthread_t thread1;pthread_create(&thread1,nullptr,run,(void*)"new thread1");pthread_t thread2;pthread_create(&thread2,nullptr,run,(void*)"new thread2");while(true){cout<<"main thread"<<endl;sleep(1);}return 0;
}

注意:
• 新线程和主线程的运行顺序是不确定的。
• 多线程的调度时间是基于对进程的时间片进行瓜分。
• 多个线程向同一个文件(这里是显示器)进行写入时,在不加保护的情况下是会发生重入的,也就会产生数据不一致问题。
• 线程是共享进程地址空间的。
• 线程出现异常会导致当前进程的其他线程全部崩溃。

1.2.2 线程终止

和进程一样,线程创建之后也是要被等待和回收的!

如果需要只终止某个线程而不终止整个进程的话,有三种方法:
• 线程执行函数return。这种方法对主线程不适用,main函数return相当于调用exit。
• 线程可以调用pthread_exit终止自己。

void pthread_exit(void *value_ptr);
//功能:线程终⽌
//参数://value_ptr:线程退出时的返回值。

需要注意pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配。因为当其它线程得到这个返回指针时线程函数已经退出了。

• 一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

int pthread_cancel(pthread_t thread);
//功能:取消一个执⾏中的线程
//参数://thread:要取消的线程ID
//返回值:成功返回0,失败返回错误码

1.2.3 线程等待

为什么需要线程等待?
因为已经退出的线程其空间没有被释放,仍然在进程的地址空间内。而创建新的线程不会复用刚才退出线程的地址空间。

int pthread_join(pthread_t thread, void **value_ptr);
//功能:等待线程结束
//参数://thread:线程ID//value_ptr:用以保存线程的返回值的地址
//返回值:成功返回0;失败返回错误码

调用该函数的线程将挂起等待,直到id为thread的线程终止。

thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的:
• 如果thread线程通过return返回,那么value_ptr所指向的单元里存放的是thread线程函数的返回值。
• 如果thread线程被别的线程调用pthread_cancel异常终掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED((void*)-1)。
• 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
• 如果对thread线程的终止状态不感兴趣,则可以传NULL给value_ptr参数。

在这里插入图片描述
示例:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;void* run1(void* arg)
{int count=5;while(count--){cout<<"new thread :"<<(char* )arg<<endl;sleep(1);}pthread_exit((void*)100);cout<<"退出成功!"<<endl;}void* run2(void* arg)
{while(true){cout<<"new thread :"<<(char* )arg<<endl;sleep(1);}
}
int main()
{//创建线程1,线程2pthread_t thread1;pthread_create(&thread1,nullptr,run1,(void*)"new thread1");pthread_t thread2;pthread_create(&thread2,nullptr,run2,(void*)"new thread2");sleep(3);//取消线程2pthread_cancel(thread2);cout<<"取消成功!"<<endl;//线程等待void *ret1,*ret2;pthread_join(thread1,&ret1);pthread_join(thread2,&ret2);cout<<"等待成功!"<<endl;cout<<"ret1 :"<<(long long)ret1<<";ret2 :"<<(long long)ret2<<endl;return 0;
}
caryon@VM-24-10-ubuntu:~/linux/thread$ ./thread 
new thread :new thread1
new thread :new thread2
new thread :new thread1
new thread :new thread2
new thread :new thread1
new thread :new thread2
取消成功!
new thread :new thread1
new thread :new thread1
等待成功!
ret1 :100;ret2 :-1

在多执行流的情况下,主执行流往往是最后退出的!

1.2.4 线程分离

默认情况下,新创建的线程是joinable的,线程退出后需要对其进型pthread_join操作,否则无法释放资源从而造成系统泄漏。如果不关心线程的返回值的话join就是一种负担。这个时候我们可以告诉系统,当线程退出时,自动释放线程资源。

int pthread_detach(pthread_t thread);
//功能:进行线程分离
//参数://thread:被分离的线程ID
//返回值:成功返回0;失败返回错误码

可以是线程组内其他线程对目标线程进型分离,也可以是线程自己分离。joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

#include <iostream>
#include <unistd.h>
#include <pthread.h>
using namespace std;void *thread_run(void *arg)
{printf("%s\n", (char *)arg);return NULL;
}
int main(void)
{//创建线程pthread_t tid;pthread_create(&tid, NULL, thread_run, (void*)"thread1 run...") ;//分离线程pthread_detach(tid);int ret = 0;sleep(1); if (pthread_join(tid, NULL) == 0){printf("pthread wait success\n");ret = 0;}else{printf("pthread wait failed\n");ret = 1;}return ret;
}
caryon@VM-24-10-ubuntu:~/linux/thread$ ./thread 
thread1 run...
pthread wait failed

二、线程id与进程空间布局

在线程创建、终止、等待时我们都有注意到pthread_t thread(线程id)这个参数的存在,这个id表示的是什么意思呢?

#include<iostream>
#include<pthread.h>
using namespace std;void* run(void* args)
{while(true);return nullptr;    
}
int main()
{pthread_t tid;pthread_create(&tid,nullptr,run,(void*)"newthread");printf("0x%lx\n",tid);return 0;
}
caryon@VM-24-10-ubuntu:~/linux/thread$ ./thread 
0x7fae58e006c0

我们可以看到这个id的值是一个地址,那这个地址是什么地址呀?

通过ldd查看我们所形成的可执行程序是依赖pthread库的,这个库在程序运行时会加载到内存中,进而映射到进程地址空间里被所有的线程所共享。我们又知道linux系统中只有轻量级进程,线程是使用轻量级进程模拟实现的。但是用户想要查看当前线程的属性怎么办呢?
linux内核中是存在这些属性的,但是Linux中不存在线程呀!这需要内核层与用户层进行解耦啊!故而线程的相关属性也是由pthread库进行维护的,用户可以通过pthread库来查看线程的属性。

所以这个地址就是该线程的属性所对应的进程地址空间中的地址!

我们可以使用下面这个函数获取当前线程的id:

pthread_t pthread_self(void);

在这里插入图片描述
上图就是进程地址空间中的tcb了。
struct pthread
这个结构里面一定封装了LWP。
线程栈
进程地址空间中的栈是主线程的栈,新线程的栈是通过动态申请创建的。
线程局部存储
理论上来说全部变量是所有线程所共享的,即他们所访问的全局变量的地址是相同的!但是在声明全局变量是加上__thread进行修饰(修饰的只能是内置类型)就表示给每个线程都来一份,即线程的局部性存储。

#include<iostream>
#include<pthread.h>
using namespace std;__thread int count=100;void* run(void* args)
{printf("new thread &count : %p\n",&count);return nullptr;    
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,run,(void*)"newthread");pthread_join(tid,nullptr);printf("main thread &count : %p\n",&count);return 0;
}
caryon@VM-24-10-ubuntu:~/linux/thread$ ./thread 
new thread &count : 0x71e23b2006bc
main thread &count : 0x71e23ba8e4fc

三、线程的封装

#pragma once#include <iostream>
#include <string>
#include <pthread.h>
#include <functional>
#include <sys/types.h>
#include <unistd.h>using func_t = std::function<void()>;
static int number = 1;
enum class TSTATUS
{NEW,RUNNING,STOP
};class Thread
{
private:// 成员方法!static void *Routine(void *args){Thread *t = static_cast<Thread *>(args);t->_status = TSTATUS::RUNNING;t->_func();return nullptr;}void EnableDetach() { _joinable = false; }bool IsJoinable() { return _joinable; }
public:Thread(func_t func) : _func(func), _status(TSTATUS::NEW), _joinable(true){_name = "Thread-" + std::to_string(number++);_pid = getpid();}bool Start(){if (_status != TSTATUS::RUNNING){int n = ::pthread_create(&_tid, nullptr, Routine, this); if (n != 0)return false;return true;}return false;}bool Stop(){if (_status == TSTATUS::RUNNING){int n = ::pthread_cancel(_tid);if (n != 0)return false;_status = TSTATUS::STOP;return true;}return false;}bool Join(){if (_joinable){int n = ::pthread_join(_tid, nullptr);if (n != 0)return false;_status = TSTATUS::STOP;return true;}return false;}void Detach(){EnableDetach();pthread_detach(_tid);}~Thread(){}
private:std::string _name;pthread_t _tid;pid_t _pid;bool _joinable; // 是否是分离的,默认不是func_t _func;TSTATUS _status;
};

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

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

相关文章

Aliyun CTF 2025 web 复现

文章目录 ezoj打卡OKoffens1veFakejump server ezoj 进来一看是算法题&#xff0c;先做了试试看,gpt写了一个高效代码通过了 通过后没看见啥&#xff0c;根据页面底部提示去/source看到源代码&#xff0c;没啥思路&#xff0c;直接看wp吧&#xff0c;跟算法题没啥关系,关键是去…

《鸿蒙系统下AI模型训练加速:时间成本的深度剖析与优化策略》

在当今数字化浪潮中&#xff0c;鸿蒙系统凭借其独特的分布式架构与强大的生态潜力&#xff0c;为人工智能的发展注入了新的活力。随着AI应用在鸿蒙系统上的日益普及&#xff0c;如何有效降低模型训练的时间成本&#xff0c;成为了开发者与研究者们亟待攻克的关键课题。这不仅关…

Git使用(一)--如何在 Windows 上安装 Git:详细步骤指南

如果你想在 Windows 机器上安装 Git&#xff0c;可以按照以下详细指南进行操作。 第一步&#xff1a;下载 Git 可通过官网下载 适用于 Windows 的 Git 最新版本。 如果下载速度较慢&#xff0c;可以通过下面提供的百度网盘 链接下载安装包&#xff0c; https://git-scm.com/d…

基于Prometheus+Grafana的Deepseek性能监控实战

文章目录 1. 为什么需要专门的大模型监控?2. 技术栈组成2.1 vLLM(推理引擎层)2.2 Prometheus(监控采集层)2.3 Grafana(数据可视化平台)3. 监控系统架构4. 实施步骤4.1 启动DeepSeek-R1模型4.2 部署 Prometheus4.2.1 拉取镜像4.2.2 编写配置文件4.2.3 启动容器4.3 部署 G…

本地Git仓库搭建(DevStar)与Git基本命令

本地Git仓库搭建&#xff08;DevStar&#xff09;与Git基本命令 实验环境搭建平台Git基本命令的使用本地仓库的创建代码提交代码合并版本发布 总结 实验环境 搭建平台 按照DevStar的Github仓库要求&#xff0c;在终端中执行下列命令&#xff0c;即可成功安装DevStar到本地部署…

stm32 蓝桥杯 物联网 独立键盘的使用

在蓝桥杯物联网平台里面&#xff0c;有5个外接设备&#xff0c;其中有一个就是6个独立按键。首先&#xff0c;我们先看一下按键有关的电路图。 电路图与cubemx设定 由图可见&#xff0c;独立键盘组由两行三列构成&#xff0c;我们通过行列来锁定要访问的独立按键在哪。ROW1挂…

set_clock_groups

一、命令参数与工具处理逻辑 核心参数定义 参数定义工具行为工具兼容性-asynchronous完全异步时钟组&#xff0c;无任何相位或频率关系&#xff08;如独立晶振、不同时钟树&#xff09;工具完全禁用组间路径的时序分析&#xff0c;但需用户自行处理跨时钟域&#xff08;CDC&a…

工作记录 2017-01-06

工作记录 2017-01-06 序号 工作 相关人员 1 协助BPO进行Billing的工作。 修改CSV、EDI837的导入。 修改邮件上的问题。 更新RD服务器。 郝 修改的问题&#xff1a; 1、 In “Full Job Summary” (patient info.), sometime, the Visit->Facility is missed, then …

Adaptive AUTOSAR UCM模块——快速入门

Adaptive AUTOSAR中的UCM模块介绍 概述 Adaptive AUTOSAR(AUTomotive Open System ARchitecture)是一个开放的行业标准,旨在为现代汽车电子系统提供一个灵活且可扩展的软件框架。在这个框架中,更新与配置管理(Update and Configuration Management, UCM)模块扮演着至关…

解决跨域问题的6种方案

解决跨域问题&#xff08;Cross-Origin Resource Sharing, CORS&#xff09;是 Web 开发中常见的需求&#xff0c;以下是 6 种主流解决方案&#xff0c;涵盖前端、后端和服务器配置等不同层面&#xff1a; 一、CORS&#xff08;跨域资源共享&#xff09; 原理 通过服务器设置…

Python Selenium库入门使用,图文详细。附网页爬虫、web自动化操作等实战操作。

文章目录 前言1 创建conda环境安装Selenium库2 浏览器驱动下载&#xff08;以Chrome和Edge为例&#xff09;3 基础使用&#xff08;以Chrome为例演示&#xff09;3.1 与浏览器相关的操作3.1.1 打开/关闭浏览器3.1.2 访问指定域名的网页3.1.3 控制浏览器的窗口大小3.1.4 前进/后…

50个经典的python库

本文整理了50个可以迅速掌握的经典Python库&#xff0c;了解它们的用途&#xff0c;无论你是刚踏上编程之路&#xff0c;还是希望在Python的世界里更加深入&#xff0c;这50个库都能帮助你快速起飞。 1. Taipy Taipy是一个开源Python库&#xff0c;用于轻松的端到端应用程序开…

【视频】V4L2、ffmpeg、OpenCV中对YUV的定义

1、常见的YUV格式 1.1 YUV420 每像素16位 IMC1:YYYYYYYY VV-- UU– IMC3:YYYYYYYY UU-- VV– 每像素12位 I420: YYYYYYYY UU VV =>YUV420P YV12: YYYYYYYY VV UU =>YUV420P NV12: YYYYYYYY UV UV =>YUV420SP(最受欢迎格式) NV21: YYYYYYYY VU VU =>YUV420SP…

freeswitch(多台服务器级联)

亲测版本centos 7.9系统–》 freeswitch1.10.9本人freeswitch安装路径(根据自己的路径进入)/usr/local/freeswitch/etc/freeswitch使用场景: 使用服务器级联需要双方网络可以ping通,也就是类似局域网内,比如A服务器IP 192.168.1.100 B服务器 192.168.1.101,通过C设备注册…

SpringMVC 基本概念与代码示例

1. SpringMVC 简介 SpringMVC 是 Spring 框架中的一个 Web 层框架&#xff0c;基于 MVC&#xff08;Model-View-Controller&#xff09; 设计模式&#xff0c;提供了清晰的分层结构&#xff0c;适用于 Web 应用开发 SpringMVC 主要组件 DispatcherServlet&#xff08;前端控…

LuaJIT 学习(1)—— LuaJIT介绍

文章目录 介绍Extensions Modulesbit.* — Bitwise operationsffi.* — FFI libraryjit.* — JIT compiler controlC API extensionsProfiler Enhanced Standard Library Functionsxpcall(f, err [,args...]) passes arguments例子&#xff1a; xpcall 的使用 load*() handle U…

std::ranges::views::common, std::ranges::common_view

std::ranges::views::common, std::ranges::common_view C20 引入的用于将范围适配为“通用范围”的工具&#xff0c;主要解决某些算法需要传统迭代器对&#xff08;如 begin 和 end 类型相同&#xff09;的问题。 基本概念 1. 功能 适配传统算法&#xff1a;将范围&#x…

4.3 数组和集合的初始及赋值

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的 版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商…

分布式光伏发电的发展现状与前景

分布式光伏发电的发展现状与前景 1、分布式光伏发电的背景2、分布式光伏发电的分类2.1、集中式光伏发电2.1.1、特点、原则2.1.2、优点2.1.3、缺点 2.2、分布式光伏发电2.2.1、特点、原则2.2.2、优点2.2.3、缺点 2.3、对比 3、分布式光伏发电的现状4、分布式光伏发电的应用场景4…

13 | 实现统一的错误返回

提示&#xff1a; 所有体系课见专栏&#xff1a;Go 项目开发极速入门实战课&#xff1b;欢迎加入 云原生 AI 实战 星球&#xff0c;12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力&#xff08;聚焦于 Go、云原生、AI Infra&#xff09;&#xff1b;本节课最终…