Linux多线程(二):线程控制

news2025/1/11 20:43:42

文章目录

    • 一、前言
    • 二、认识线程控制函数
      • 1.线程创建
      • 2.线程退出
      • 3.线程等待
      • 4.查看线程id
      • 5.线程分离
      • 6.综合demo
    • 三、线程id本质是地址?

一、前言

 上篇博客谈到,Linux并没有真线程,而是通过复用进程的数据结构来模拟实现线程的。因此 Linux 自然不会提供线程操作函数,只提供了创建轻量级级进程的系统接口,例如clone、vfork函数。
 但是对于用户来说,创建一个线程还需要自己维护和管理,使用成本太高,于是有人基于Linux在应用层编写了 pthread库 用于方便创建和管理多线程。通过调用OS提供的创建轻量级进程的系统接口,为上层用户提供应用级的线程接口。
 由于pthread不是C原生的库,在使用g++编译链接的时候需要带上 -lpthread 选项。不理解为什么要带上这个选项的同学,可以参见之前的博客:Linux基础IO(四):动静态库的制作与使用
在这里插入图片描述

二、认识线程控制函数

1.线程创建

image-20221201152205788
[作用]: 线程创建
[参数说明]:

  • thread:返回型参数。用于返回线程ID
  • attr:设置线程的属性,attr为NULL表示使用默认属性,一般使用默认即可
  • start_rountine:指定线程启动后要执行的函数。注意该函数的参数和返回值类型都是 void*
  • arg:传递给线程的参数,注意要类型要强转为 void*

[返回值]: 成功返回0,失败返回错误码

 我们在前一篇博客里提到,线程只执行进程的部分代码。这如何实现的呢?此时你就理解了:我们通过传入函数指针的方式,让一个线程只执行该函数的代码

2.线程退出

在这里插入图片描述
[作用]: 退出线程,并通过 retval 返回线程的退出状态

在这里插入图片描述
【作用】: 用于终止指定线程

使用 return 也可以终止线程,return的结果就作为线程的退出状态:

void* rouRoutine(void* arg)
{
	// ……
	return (void*)10;
}

3.线程等待

在这里插入图片描述
[作用]: 线程等待,获取线程的退出状态;回收资源,避免内存
[返回值]: 成功返回0,错误返回错误码
[参数说明]:

  • thread:线程ID
  • retval:返回性参数。当retval不为NULL时,返回指定线程的退出状态(不能获取退出信号)。由于线程退出状态为void*,因此需要用 void** 的指针去接收

[使用说明]: 如果指定线程已经被退出了,函数会立即返回;否则将会阻塞等待

void* rouRoutine(void* arg)
{
	// ……
	return (void*)10;
}

int main()
{
	// ……
	void* retval;
	pthread_join(tid, &retval);
    cout << (long int)retval << endl; ;
}

(说明:Linux系统为64位,指针大小为8字节。64位平台下,long int 也是8字节,因此我们可以将指针强转为long int输出打印线程的退出状态)

[问题一]: 线程出异常了怎么办?不用获取退出信息吗?
 线程就是进程的一部分,一个线程出异常,整个进程都会退出,也就是说线程异常 = 进程异常。当线程发生异常时,退出信息由父进程获取。

4.查看线程id

在这里插入图片描述
[作用]: 返回调用线程的id

// 使用案例:
void* runRoutine(void* arg)
{
    pthread_t tid = pthread_self();
    printf("%p\n", tid);
    return nullptr;
}
// 某一次的输出结果:0x7ffb5ccb2700

[问题二]:使用 ps -aL指令也可以查看线程id,两者有什么区别?
在这里插入图片描述
LWP (light weight process - 轻量级进程) 对应的就是每个线程的标识符,类似于 PID,是个操作系统使用的,用于标识每个线程的唯一性。
 使用 pthread_self() 函数查看到的用户级线程id,本质上是一个地址,是给用户操作使用的。至于为什么是地址,这里埋个伏笔,我们将在后文中具体阐述。

[问题三]:能不能编写代码查看LWP
 可以使用函数gettid()获取,但是该函数是个半成品,不能直接使用,必须通过系统调用来使用:

int main()
{
   pthread_t thread;
   pthread_create(&thread, nullptr, func, (void*)"1");
   cout << syscall(SYS_gettid) << endl;
   pthread_join(thread, nullptr);
}   

5.线程分离

 默认情况下,新创建的线程是 joinable可结合的。在线程退出后,需要对其进行 pthread_join 操作,否则无法释放资源,从而造成内存泄漏。但是当我们不关心线程的返回值时,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源 。使用下面的函数就可以实现线程分离,传入线程id即可使用。
在这里插入图片描述

[问题四]:分离后的线程是不能被等待的,但下面的代码为什么没有报错?

void* func(void* arg)
{
  	pthread_detach(pthread_self());
  	for(int i = 0; i < 5; i++)
 	{
   		cout << "我是子线程" << endl;
	}
	return nullptr;
}

int main()
{
		pthread_t thread;
		pthread_create(&thread, nullptr, func, (void*)"thread 1");

		int n = pthread_join(thread, nullptr);
		cout << strerror(n) << endl;
}

image-20221203163522315
 哪个线程先执行由调度器决定,是不确定的。在子线程运行 pthread_detach() 之前,主线程可能已经可能已经往下执行,开始阻塞式地等待线程退出了。如果在 join 之前 sleep(1) 就能看到正确的看到错误。
image-20221203163913801
借这个问题,我也想提醒大家:在分离线程的时候,尽可能在主线程中分离

6.综合demo

void* startRoutine(void* arg)
{
    char* name = static_cast<char*>(arg);
    cout << name << " tid = " << pthread_self() << endl;
    return (void*)1;
}

int main()
{
    pthread_t tid1;   // 线程分离
    pthread_t tid2;   // 线程不分离
    void* retval;

    pthread_create(&tid1, nullptr, startRoutine, (void*)"thread 1");
    pthread_create(&tid2, nullptr, startRoutine, (void*)"thread 2");

    pthread_detach(tid1);    
    pthread_join(tid2, &retval);
    cout << (long int) retval << endl;
    return 0;
}

三、线程id本质是地址?

线程是对进程空间资源的划分,具体是如何划分的呢,我们不难形成这样的认识:

  1. 代码区以函数的形式划分
  2. 全局数据各个线程之间共享
  3. 申请的堆空间对于其他线程来说是可见的

 但是,线程具有独立的栈空间,那么如何理解线程的独立栈呢?我们在前言中提到,pthread库通过调用OS提供的创建轻量级进程的系统接口,为上层用户提供应用级的线程接口。
 因此,线程的全部实现,并没有完全体现在OS内,OS只是提供了执行流,具体的线程结构是由库来进行管理。既然要管理线程,依照先描述后组织的设计思想,库需要设计出线程相应的数据结构来实现对线程的管理:

union pthread_attr_t
{
  char __size[__SIZEOF_PTHREAD_ATTR_T];  // 私有栈
  long int __align;                      // tid
};

 动态库是加载到进程地址空间的共享区中的,因此我们每创建一个线程,库就在共享区中为我们创建一个线程结构体,并将线程结构体的起始地址返回供用户操作使用。因此pthread_t本质就是该结构体的起始地址。拿到线程的起始地址后,我们就可以访问结构体的各个成员了。image-20221203154447726

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

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

相关文章

所有的为时已晚都是恰逢其时,社科院与杜兰大学金融管理硕士邀你在职读研

最近有咨询的同学询问&#xff0c;我年龄快35岁了读研晚吗&#xff1f;记得在网上看到过一句话&#xff0c;你觉得为时已晚的时候&#xff0c;恰恰是最早的时候。你可以确定一下你的内心&#xff0c;是不是真的想读研&#xff0c;既然迟早要读的话&#xff0c;与其等到了40、50…

ISO 15765-2协议分享(一)- TP时间参数详解

文章目录 前言一、时间参数协议定义二、使用步骤 1.引入库2.读入数据总结前言 无大志者常立志,让自己生活中时时有目标,有努力的方向。 老规矩,正文前分享喜欢的文字: 生活中,你越是去竭力回避不适的感觉,就越是难以推进生活中的重要转变。 不回避,积极面对,是提升自…

蓝奥声网关为什么会受到广大用户的欢迎?

蓝奥声GP20蓝牙网关是一款支持无线和有线连接的智能网关&#xff0c;配有独特的网口驱动&#xff0c;支持带有TCP/IP的以太网接口。用于互联网访问和与主机服务器的通信&#xff0c;用于远程命令和本地处理数据的数据上传/下载。它支持 RJ45/POE 和 WiFi 两种网络角色。 GP20蓝…

点面科技荣获优胜企业奖,圆满从2022“创·在上海”国际创新创业大赛之“创·加速营”毕业

创在上海 赢在未来 “创在上海”国际创新创业大赛暨中国创新创业大赛 (上海赛区) 是一项具有广泛影响力的创新创业活动。 为提升入围全国赛的参赛企业的参赛能力&#xff0c;“创在上海”将充分整合和调动多方资源&#xff0c;结合国赛进程&#xff0c;创新推出针对入围国赛企…

Git常用命令总结

配置git 本地创建 ssh key&#xff1a; ssh keygen -t rsa -c “email” // email:github上注册用的邮箱 把上面生成的 key 粘贴到 gitHUb中的 sshkey中 验证 是否成功 ssh -T gitgithub.com 如果是第一次的会 提示 是否continue &#xff0c;输入 yes 就会看到 You’ve succe…

Springboot健康上报小程序: element后台管理系统(完整代码)

这几天接到了个学生的需求&#xff0c;挺简单的&#xff0c;大概就是按照她的需求做一个疫情期间常态化管理的小程序&#xff0c;由于我对java不熟悉&#xff0c;基本上是边做边学&#xff0c;这里我将对本次项目做个记录 ✨✨欢迎订阅本专栏或者关注我&#xff0c;大家一起努力…

内科大机器学习框架课程重点内容笔记

文章目录一.背景二.内容一.背景 这是2020届内科大机器学习框架课程的考试复习内容 二.内容 1.变量间的相互关系&#xff1a; (1)确定性关系或函数关系&#xff1a;研究的是确定现象非随机变量间的关系。 (2)相关关系或统计依赖关系&#xff1a;研究的是非确定现象随机变量间…

计算机毕设Python+Vue校园一卡通管理系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

外滩金融峰会公布“第七届融城杯十佳案例”,工行、创邻科技等成功上榜

12月11日&#xff0c;由中国金融四十人论坛&#xff08;CF40&#xff09;主办的第四届外滩金融峰会在上海召开。国家副主席王岐山为峰会致开幕辞&#xff0c;中国人民银行原行长周小川、上海新金融研究院理事长屠光绍、中国人民银行副行长宣昌能、中央财经委员会办公室副主任尹…

存储mybatis的xml标签,动态sql 查询

前言&#xff1a; 通过表动态存储mybatis 的xml标签&#xff0c;通过动态sql 入参查询&#xff0c;方便更新查询逻辑&#xff0c;无需发版即可&#xff1b;&#xff08;当前用的是 mybatis-plus &#xff0c;db用的是oracle【这个无所谓】&#xff09; 注意事项&#xff1a;这…

【C】带你复习有趣的函数

作者 &#xff1a;会敲代码的Steve 墓志铭&#xff1a;博学笃志&#xff0c;切问静思。 前言&#xff1a;本文旨在总结C语言函数章节的知识点、分为以下九个模块、分别是&#xff1a; 1.函数是什么 2.库函数 3.自定义函数 4.函数参数 5.函数调用 6.函数的嵌套调用和链式…

vue3计算属性和侦听与script setup区别使用

一、计算属性computed 计算属性&#xff0c;只要依赖值不变&#xff0c;那么不会重新计算计算属性将基于它们的反应依赖关系缓存&#xff0c;提高性能对于任何包含响应式数据的复杂逻辑&#xff0c;应该使用计算属性 <template><!--重复使用--><p>{{ msg.s…

Redis——好友关注、共同关注、Feed流推送

1. 好友关注 在探店图文的详情页面中&#xff0c;可以关注发布笔记的作者&#xff1a; 进到探店笔记详情页&#xff0c;会发出两个请求&#xff0c;1是判断是否已经关注&#xff0c;2是尝试关注用户的请求。 关注是User之间的关系&#xff0c;是博主与粉丝的关系&#xff0c;…

你用过猿如意吗?猿如意可以使用ChatGPT哦,这里详细介绍了猿如意的功能,为什么我建议你使用猿如意,来看看吧

文章内容介绍 你是否还在为为每次安装IDE&#xff08;集成开发工具&#xff09;要去各种网站找教程而烦恼&#xff1f;你是否还在为各种文本格式转换而头痛&#xff1f;你是否在为斗图都不过兄弟们而卑微&#xff1f;你是否在为互联网中庞大冗杂却低效的教程文档而崩溃&#x…

SpringMVC:SpringMVC响应结果(7)

响应结果1. 环境准备2. 响应页面3. 响应文本数据4. 响应JSON数据&#xff08;掌握&#xff09;4.1 响应POJO对象4.2 响应POJO集合对象1. 环境准备 项目结构 pom.xml添加Spring依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"…

Mathtype / Word OMML / Latex 公式相互转换

文章目录Part.I 快应用Part.II 详细操作Chap.I Mathtype ↔ WordChap.II Mathtype ↔ LatexChap.III Latex ↔ WordPart.I 快应用 本文介绍了除了使用 Word 和 Mathtype 之外不使用其他任何辅助软件来实现三者相互转换的方法。 项目操作方法Mathtype 转成 Word OMMLWord菜单栏→…

信息化课堂怎么控屏教学的

现在的很多学校都在建设机房进行互动教学&#xff0c;相比于传统的教学方法&#xff0c;计算机的教学方式能够直观的表达每堂课的知识点&#xff0c;过程更为新颖&#xff0c;有利于吸引学生的注意力&#xff0c;提高在学习过程中的专注力。 但是&#xff0c;在提高增加课堂中学…

redo log 和binlog的相关问题及其衍生

目录 下在两阶段提交的不同时刻&#xff0c;MySQL异常重启会出现什么现象。 那么&#xff0c; MySQL怎么知道binlog是完整的? redo log 和 binlog是怎么关联起来的? 处于prepare阶段的redo log加上完整binlog&#xff0c;重启就能恢复&#xff0c;MySQL为什么要这么设计? …

ensp环境 AC+AP组网及ACweb界面配置

1.进入交换机划分Vlan并且配置好每条链路 The device is running! <Huawei>system-view [Huawei]sysname SW1 [SW1]vlan batch 100 101 [SW1]interface GigabitEthernet 0/0/2 [SW1-GigabitEthernet0/0/2]port link-type trunk [SW1-GigabitEthernet0/0/2]port trunk…

C++:类和对象:对象的初始化和清理

1 前言&#xff1a; 构造和析构的背景 1&#xff1a;C中的面向对象来源于生活&#xff0c;每个对象都会有初始值以及对象销毁前的清理数据设置 2&#xff1a;对象的初始化和清理是两个非常重要的安全问题&#xff0c;一个对象或者变量没有初始状态&#xff0c;对其使用后果是未…