【Linux】—— 线程控制的基本介绍

目录

(一)POSIX线程库

(二)创建线程

2.1 线程ID及进程地址空间布局

(三)线程终止

(四)分离线程


(一)POSIX线程库

POSIX线程库(POSIX Threads Library),通常简称为Pthreads,是一个为POSIX操作系统(包括UNIX和类UNIX系统如Linux)提供线程支持的库。Pthreads定义了一组API,允许程序员创建、管理、同步和销毁线程。 

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

(二)创建线程

【函数介绍】 

功能:创建一个新的线程原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void*), void *arg);参数thread:返回线程IDattr:设置线程的属性,attr为NULL表示使用默认属性start_routine:是个函数地址,线程启动后要执行的函数arg:传给线程启动函数的参数返回值:成功返回0;失败返回错误码

错误检查

  1. 对于Pthreads函数,如果调用失败,通常会返回一个非零的错误代码,你可以使用这个错误代码来查询具体是什么错误发生了。为了将错误代码映射到人类可读的错误消息,你可以使用strerrorpthread_strerror函数(如果可用)。

  2. 另外,Pthreads库确实为每个线程提供了一个私有的errno变量,这允许线程安全地访问errno。然而,对于Pthreads函数本身来说,直接检查返回值通常是更好的做法,因为这样可以避免任何可能的线程间干扰,并且通常更加高效。

 【代码示例】 

#include <iostream>
#include <unistd.h>
#include <pthread.h>using namespace std;int g_val = 0; void *threadRun1(void *args)
{while (true){sleep(1);cout << "t1 thread..." << getpid() << " &g_val: " << &g_val << " , g_val: " << g_val << endl;}
}void *threadRun2(void *args)
{while (true){sleep(1);cout << "t2 thread..." << getpid()  << " &g_val: " << &g_val << " , g_val: " << g_val++ << endl;}
}int main()
{pthread_t t1, t2;pthread_create(&t1, nullptr, threadRun1, nullptr);pthread_create(&t1, nullptr, threadRun2, nullptr);while (true){sleep(1);cout << "main thread..." << getpid()  << " &g_val: " << &g_val << " , g_val: " << g_val << endl;}
}

2.1 线程ID及进程地址空间布局

  1. pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。
  2. 前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
  3. pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
  4. 线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID

【函数介绍】 

pthread_t pthread_self(void);

错误检查

  • 如果成功,pthread_self 返回调用线程的线程 ID。如果发生错误,这个函数没有定义错误码返回机制,因为它总是应该成功。

  【代码示例】 

#include <iostream>  
#include <pthread.h>  
#include <unistd.h>using namespace std;void* threadRun(void* arg) {  pthread_t id = pthread_self(); // 获取当前线程的 ID  cout << "the thread ID is :  " << id << endl;  sleep(1);  return nullptr;  
}  int main() {  pthread_t tid;  int ret;  // 创建线程  ret = pthread_create(&tid, nullptr, threadRun, nullptr);  if (ret != 0) {  cerr << "Error: pthread_create() failed with error " << ret << endl;  return 1;  }  // 等待线程完成  pthread_join(tid, nullptr);  cout << "Main thread exiting." << endl;  return 0;  
}

 pthread_t 到底是什么类型呢?取决于实现。对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址


(三)线程终止

如果需要只终止某个线程而不终止整个进程 , 可以有三种方法 :
  • 1. 从线程函数return。这种方法对主线程不适用,main函数return相当于调用exit
#define NUM 10void *threadrun(void *args)
{char *name = (char*)args;while (true){cout << "new thread run, the new thread name is : " << name   << endl;sleep(3);break;}delete name;return  nullptr;
}int main()
{pthread_t tids[NUM];for(int i= 0; i< NUM; ++i){char *tname = new char[64];snprintf(tname,64,"thread-%d",i+1);pthread_create(tids+i,nullptr,threadrun,tname);}void *ret = nullptr;for(int i= 0; i< NUM; ++i){int n = pthread_join(tids[i],nullptr);if(n != 0) cerr << "pthread_join error" << endl;cout << "thread quit: " <<(uint64_t)ret << endl;}cout << "all thread quit..."<<endl; return 0;
}
  • 2. 线程可以调用pthread_ exit终止自己。

【函数介绍】 

功能:线程终止
原型void pthread_exit(void *value_ptr);
参数value_ptr:value_ptr不要指向一个局部变量。返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
  • 需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

 【代码示例】 

#define NUM 10void *threadrun(void *args)
{char *name = (char*)args;while (true){cout << "new thread run, the new thread name is : " << name   << endl;sleep(3);break;}delete name;pthread_exit((void*)1); 
}int main()
{pthread_t tids[NUM];for(int i= 0; i< NUM; ++i){char *tname = new char[64];snprintf(tname,64,"thread-%d",i+1);pthread_create(tids+i,nullptr,threadrun,tname);}void *ret = nullptr;for(int i= 0; i< NUM; ++i){int n = pthread_join(tids[i],nullptr);if(n != 0) cerr << "pthread_join error" << endl;cout << "thread quit: " <<(uint64_t)ret << endl;}cout << "all thread quit..."<<endl; return 0;
}
  • 3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。

【函数介绍】 

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

  【代码示例】 

void *threadRun(void* args)
{const char*name = static_cast<const char *>(args);int cnt = 5;while(cnt){cout << name << " is running: " << cnt-- << " obtain self id: " << pthread_self() << endl;sleep(1);}pthread_exit((void*)11); // PTHREAD_CANCELED; #define PTHREAD_CANCELED ((void *) -1)
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRun, (void*)"thread 1");sleep(3);pthread_cancel(tid);void *ret = nullptr;pthread_join(tid, &ret);cout << " new thread exit : " << (int64_t)ret << "quit thread: " << tid << endl;return 0;
}

【输出结果】(三秒后取消操作)


其次就是在上述代码中,使用了线程等待函数,那为什么需要线程等待?  

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复用刚才退出线程的地址空间。

 【函数介绍】

功能:等待线程结束原型int pthread_join(pthread_t thread, void **value_ptr);参数
thread:线程ID
value_ptr:它指向一个指针,后者指向线程的返回值返回值:成功返回0;失败返回错误码
调用该函数的线程将挂起等待 , 直到 id thread 的线程终止。 thread 线程以不同的方法终止 , 通过 pthread_join 得到的终止状态是不同的,总结如下:
  • 1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  • 2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_ CANCELED。
  • 3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
  • 4. 如果对thread线程的终止状态不感兴趣,可以传NULLvalue_ ptr参数。


(四)分离线程

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

 【函数介绍】

int pthread_detach(pthread_t thread);

【代码示例】 

void* threadRun(void* args) {   sleep(2);  cout << "线程任务已完成\n";  return nullptr;  
}  int main() {  pthread_t tid;  int ret;  // 创建线程  ret = pthread_create(&tid, nullptr, threadRun, nullptr);  if (ret) {  cerr << "Error: pthread_create() failed\n";  return 1;  }  // 分离线程  pthread_detach(tid);  // 主线程继续执行  cout << "主线程继续执行...\n";  // 主线程休眠一段时间以便观察分离线程的输出  sleep(3);  // 主线程结束,分离线程的资源会在其完成后由系统自动回收  return 0;  
}

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

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

相关文章

Node.js后端构建指南:MongoDB与Express的集成

安装express 安装 Express 并将其保存到依赖列表中&#xff1a; $ cnpm install express --save 以上命令会将 Express 框架安装在当前目录的 node_modules 目录中&#xff0c; node_modules 目录下会自动创建 express 目录。以下几个重要的模块是需要与 express 框架一起安…

nss刷题(4)

1、[SWPUCTF 2021 新生赛]easyrce <?php error_reporting(0); highlight_file(__FILE__); if(isset($_GET[url])) { eval($_GET[url]); } ?> if(isset($_GET[url])) isset函数用来检测url变量是否存在&#xff1b;$_GET函数获取变量数据 eval($_GET[url]); eval函数用…

【GIS矢量切片】tippecanoe在Windows和CentOS中的安装

组件安装记录 背景介绍Windows下安装1、下载工具2、存放安装包3、进入DOS终端4、在终端执行命令5、下载程序6、放置源码7、修改配置信息8、编译9、测试10、参数说明瓦片输出瓦片描述和权属信息输入文件和图层名输入文件的并行处理输入文件的投影缩放级别瓦片分辨率CentOS 7安装…

第三篇——大数据思维的科学基础

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 大数据时代&#xff0c;大数据思维的重要性不言而喻&#xff1b;而信息在…

6月26~28日,2024北京国际消防展即将开幕!

随着社会的快速发展&#xff0c;消防安全日益受到广大民众的高度关注。为了进一步推动消防科技的创新与发展&#xff0c;提升全民消防安全意识&#xff0c;2024年北京消防展将于6月26日在北京国家会议中心盛大开展。目前:观众预登记已全面启动&#xff0c;广大市民和业界人士可…

SQLite3(1):介绍安装与测试

目录 1、SQLite3介绍 2、SQLite3的优势和特性 3、SQLite3安装与测试 3.1 SQLite3安装 3.2 SQLite3测试 4、SQLite3简单使用 4.1 连接数据库文件 4.2 创建信息表 4.3 插入三个学生信息 4.4 确认信息 5、总结 1、SQLite3介绍 SQLite3是一种轻量级的关系型数据库管理系…

论文阅读 A Distributional Framework for Data Valuation

本论文解决的问题 量化数据价值&#xff08;机器学习模型训练中各个数据点的贡献&#xff09; 避免数据价值受到其所处数据集的影响&#xff0c;使数据点的估值更加稳定、一致 变量假设 假设 D 表示一个在全集 Z 上的数据分布。对于监督学习问题&#xff0c;我们通常认为 Z…

jvm学习笔记(一) ----- JAVA 内存

JAVA 内存 一、程序计数器二、虚拟机栈三、本地方法栈四、堆五、非JAVA内存(堆外内存)1.元空间(Metaspace)2.直接内存 链接: jvm学习笔记(二) ----- 垃圾回收 链接: jvm学习笔记(三) ----- 垃圾回收器 一、程序计数器 虚拟机需要通过『程序计数器』记录指令执行到哪了。线程要…

代码随想录算法训练营day43

题目&#xff1a;1049. 最后一块石头的重量 II 、494. 目标和、474.一和零 参考链接&#xff1a;代码随想录 1049. 最后一块石头的重量 II 思路&#xff1a;本题石头是相互粉碎&#xff0c;粉碎后剩下的重量就是两块石头之差&#xff0c;我们可以想到&#xff0c;把石头分成…

使用智谱 GLM-4-9B 和 SiliconCloud 云服务快速构建一个编码类智能体应用

本篇文章我将介绍使用智谱 AI 最新开源的 GLM-4-9B 模型和 GenAI 云服务 SiliconCloud 快速构建一个 RAG 应用&#xff0c;首先我会详细介绍下 GLM-4-9B 模型的能力情况和开源限制&#xff0c;以及 SiliconCloud 的使用介绍&#xff0c;最后构建一个编码类智能体应用作为测试。…

数据结构和算法之数组和链表

一、数组 数组是一种线性数据结构&#xff0c;它是由一组连续的内存单元组成的&#xff0c;用于存储相同类型的数据。在JavaScript中&#xff0c;数组可以包含任意类型的数据&#xff0c;不只限于基本数据类型。 1.存储方式 在内存中&#xff0c;数组的元素是连续存储的&…

【Vue】组件的存放目录问题

注意&#xff1a; .vue文件 本质无区别 组件分类 .vue文件分为2类&#xff0c;都是 .vue文件&#xff08;本质无区别&#xff09; 页面组件 &#xff08;配置路由规则时使用的组件&#xff09;复用组件&#xff08;多个组件中都使用到的组件&#xff09; 存放目录 分类开来的…

Llama模型家族之拒绝抽样(Rejection Sampling)(二)均匀分布简介

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…

ssti模板注入

一、Flask应用 1、介绍 定义 Flask&#xff1a;是一个使用Python编写的轻量级web应用框架。Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。 特点 良好的文档、丰富的插件、包含开发服务器和调试器、集成支持单元测试、RESTful请求调度、支持安全cookies、基于Unicode。 …

手机短信删除怎么恢复?快速找回的3个秘密武器

手机&#xff0c;这个我们每天离不开的小玩意儿&#xff0c;有时候也会让我们头疼不已。比如&#xff0c;你一不小心&#xff0c;或者为了清理点空间&#xff0c;就把那些重要的短信给删了。这些短信可能是你和好友的深夜聊天&#xff0c;或者是重要的工作信息。一旦删除&#…

哪款开放式耳机佩戴最舒服?2024五款备受推崇产品分享!

​在现今耳机市场&#xff0c;开放式耳机凭借其舒适的佩戴体验和独特的不入耳设计&#xff0c;备受消费者追捧。它们不仅让你在享受音乐时&#xff0c;仍能察觉周围的声音&#xff0c;确保与人交流无障碍&#xff0c;而且有利于耳朵的卫生与健康。对于运动爱好者和耳机发烧友而…

GIGE 协议摘录 —— 引导寄存器(四)

系列文章目录 GIGE 学习笔记 GIGE 协议摘录 —— 设备发现&#xff08;一&#xff09; GIGE 协议摘录 —— GVCP 协议&#xff08;二&#xff09; GIGE 协议摘录 —— GVSP 协议&#xff08;三&#xff09; GIGE 协议摘录 —— 引导寄存器&#xff08;四&#xff09; GIGE 协议…

前后端实现文件上传进度条-实时进度

后端接口代码&#xff1a; PostMapping("/upload")public ResponseEntity<String> handleFileUpload(RequestParam("file") MultipartFile file) {try {// 获取文件名String fileName file.getOriginalFilename();// 创建上传目标路径Path targetPa…

基于简单Agent对医疗数据进行分析

数据表 供应商资格审核规定.pdf 医生名录.xlsx 历史就诊记录.xlsx 患者信息名录.xlsx 药品.xlsx 药品库存管理.xlsx 采购单位基本信息.xlsx Agent测试 模型基于ChatGPT-3.5 问题&#xff1a;帮我找出不达标的供应商 Agent分析过程 [Thought: 0] Key Concepts: - 不达标的供…

嵌入式Linux系统中RTC应用的操作详解

第一:RTC的作用以及时间简介 “RTC”的英文全称是Reul-Time Clock,翻译过来是实时时钟芯片.实时时钟芯片是日常生活中应用最为广泛的电子器件之一,它为人们或者电子系统提供精确的实时时间,实时时钟芯片通过引脚对外提供时间读写接口,通常内部带有电池,保证在外部系统关…