【Linux操作系统】多线程控制(创建,等待,终止、分离)

目录

  • 一、线程与轻量级进程的关系
  • 二、进程创建
    • 1.线程创建
      • 线程创建函数(pthread)
      • 查看和理解线程id
      • 主线程与其他线程之间的关系
  • 三、线程等待(回收)
  • 四、线程退出
    • 线程退出情况
    • 线程退出方法
  • 五、线程分离
  • 线程的优点
  • 线程的缺点

一、线程与轻量级进程的关系

首先我想问一个问题,Linux中有没有真线程呢? 答案是没有,Linux中只有轻量级进程

但是用户不知道“轻量级进程”,认为只有进程和线程。因此,Linux系统中不会有与线程相关的系统调用,只有轻量级进程的系统调用

Linux系统为了能让用户进行正常的使用Linux线程,Linux设计者在用户与内核之间设计了一个 pthread库—原生线程库

**作用是将轻量级进程的系统调用进行封装,转成线程相关的接口语义提供给用户,让用户感觉自己使用的是线程,但实际上底层是轻量级进程 **,所以我们平时在Linux中使用线程必须带上这个库, 不过要注意的是这个库并不属于LInux内核,只要是库它就是在用户级实现的,因此有许多人将Linux的线程称作“用户级线程”,所有线程的实现都是在 用户级实现的

二、进程创建

1.线程创建

线程创建函数(pthread)

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>
#include <sys/types.h>

void *newthreadrun(void *args)
{
    while (true)
    {
        std::cout << "I am new thread, pid: " << getpid() << std::endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, newthreadrun, nullptr);

    while (true)
    {
        std::cout << "I am main thread, pid: " << getpid() << std::endl;
        sleep(1);
    }
}

有个注意事项就是,如果我们创建线程传递的是 类成员函数 ,那么就需要将我们的类成员函数声明为static函数,因为如果类成员函数的参数中默认会有一个this指针,将类成员函数声明为static函数,这个成员函数就没有this指针了,否则就会造成匹配错误

查看和理解线程id

使用 ps -aL 查看

在这里插入图片描述
当我们使用 ps -aL 查看进程时我们会发现除了我们熟知的PID,还有一个陌生的LWP

如果这个进程是单进程的话,这个PID和LWP是一样的
如果这个进程是多线程的话,这个LPW代表不同线程(轻量级进程(light weight process))LWP
其中这个PID是主线程的ID

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

线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID:

pthread_t pthread_self(void);

主线程与其他线程之间的关系

  1. 主线程与其他线程谁先运行?

这个答案是不确定的,由调度器决定

  1. 主线程退出,其他线程继续运行还是退出?

主线程退出,就等同于这个进程退出了,而进程是承担分配系统资源的基本实体,进程退出了,进程所拥有的内部资源也应该被释放,也包括内部的执行流等,所以只要主线程退出了,所有进程都要退出

  • 这也意味着我们的主线程需要最后结束

三、线程等待(回收)

前面我们学过,进程在退出时需要回收它(wait),否则就会变成僵尸进程,而线程同样如此,线程也需要被回收(wait),否则也会造成内存泄漏问题

线程等待函数 :pthread_join

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。
  • 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数
  • 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数

四、线程退出

线程退出情况

线程退出无非三种情况:

  1. 代码跑完,结果对
  2. 代码跑完,结构错
  3. 代码出异常

重点是出异常情况:

如果在多线程中,任意一个进程出现异常,都会导致整个进程退出,因此有些人说多线程代码往往健壮性不好

线程退出方法

  • 使用return

从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。

  • 使用pthread_exit函数

线程可以调用pthread_ exit终止自己,类似于进程退出的exit

void pthread_exit(void *value_ptr);

无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

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

  • 使用pthread_cancel函数

取消一个执行中的线程

int pthread_cancel(pthread_t thread);
  • thread:线程ID
  • 返回值:成功返回0;失败返回错误码

五、线程分离

默认情况下,新创建的线程是joinable的,也就是说,在线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏,导致类似僵尸进程的事件

但如果我们的主线程并不关心其他线程的返回值,join它是一种负担,这个时候,我们可以告诉系统,当该线程退出时,自动释放该线程资源,也就是说,我们可以将这个线程分离出去,可以说使该线程与主线程达到真正的并行

不过所谓的分离只是线程的一种工作状态,并不是说页表、进程地址空间等分离了,底层依旧属于同一个进程,因此当主线程退出时,分离线程还是会跟着退出,只是不需要等待了

线程并行方法:

  • 在主线程分离其他线程
int pthread_detach(pthread_t thread);
  • 在该线程中分离线程
pthread_detach(pthread_self());

举例:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <pthread.h> 
void* thread_run(void* arg)
{
	pthread_detach(pthread_self());
	printf("%s\n", (char*)arg);
	return NULL;
}
int main(void)
{
	pthread_t tid;
	if (pthread_create(&tid, NULL, thread_run, "thread1 run...") != 0) {
		printf("create thread error\n");
		return 1;
	}
	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;
}

线程的优点

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多
  • 能充分利用多处理器的可并行数量
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

线程的缺点

  • 性能损失

一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型
线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的 同步和调度开销,而可用的资源不变。

  • 健壮性降低

编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了
不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

  • 缺乏访问控制

进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

  • 编程难度提高

编写与调试一个多线程程序比单线程程序困难得多

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android ConstraintLayout 约束布局的使用手册

目录 前言 一、ConstraintLayout基本介绍 二、ConstraintLayout使用步骤 1、引入库 2、基本使用&#xff0c;实现按钮居中。相对于父布局的约束。 3、A Button 居中展示&#xff0c;B Button展示在A Button正下方&#xff08;距离A 46dp&#xff09;。相对于兄弟控件的约束…

【论文复现】隐式神经网络实现低光照图像增强

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀ 隐式神经网络实现低光照图像增强 引言那么目前低光照图像增强还面临哪些挑战呢&#xff1f; 挑战1. 不可预测的亮度降低和噪声挑战2.度量友好…

【机器学习】机器学习的基本分类-监督学习-决策树-C4.5 算法

C4.5 是由 Ross Quinlan 提出的决策树算法&#xff0c;是对 ID3 算法的改进版本。它在 ID3 的基础上&#xff0c;解决了以下问题&#xff1a; 处理连续型数据&#xff1a;支持连续型特征&#xff0c;能够通过划分点将连续特征离散化。处理缺失值&#xff1a;能够在特征值缺失的…

Spring和SpringBoot的关系和区别?

大家好&#xff0c;我是锋哥。今天分享关于【Spring和SpringBoot的关系和区别&#xff1f;】面试题。希望对大家有帮助&#xff1b; Spring和SpringBoot的关系和区别&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring和Spring Boot是两种相关但有所…

Scrapy 中的配置笔记

概述 scrapy在命令启动之前&#xff0c;先设置好了各种配置文件。其中包括系统自带的默认配置文件&#xff0c;还有用户自定义的settings.py。其中还有一个日常开发中不怎么用的scrapy.cfg文件&#xff0c;这个文件是用来告诉scrapy用户自定义的settings.py文件在哪里的 关键…

代码随想录算法训练营day49|动态规划part11

最长公共子序列 这个与上篇笔记最大的不同就是子序列里的数可以不相邻,那么只需加入一个dp[i][j]的上和左的更新方向即可 class Solution { public:int longestCommonSubsequence(string text1, string text2) {vector<vector<int>> dp(text1.size()1,vector<…

Python知识分享第十九天-网络编程

网络编程 概述用来实现 网络互联 不同计算机上运行的程序间可以进行数据交互也叫Socket编程 套接字编程 三要素IP地址概述设备在网络中的唯一标识分类IPV4城域网13广域网22局域网31IPV6八字节 十六进制相关dos命令查看ipwindows: ipconfigmac和linux: ifconfig测试网络ping 域…

CAN接口设计

CAN总线的拓扑结构 CAN总线的拓扑结构有点像485总线,都是差分的传输方式,总线上都可以支持多个设备,端接匹配电阻都是120Ω。 485和CAN通信方面最大的区别:网络特性。485是一主多从的通讯方式,CAN是多主通讯,多个设备都可以做主机。那多个设备都相要控制总线呢?…

Latex转word(docx)或者说PDF转word 一个相对靠谱的方式

0. 前言 投文章过程中总会有各种各样的要求&#xff0c;其中提供word格式的手稿往往是令我头疼的一件事。尤其在多公式的文章中&#xff0c;其中公式转换是一个头疼的地方&#xff0c;还有很多图表&#xff0c;格式等等&#xff0c;想想就让人头疼欲裂。实践中摸索出一条相对靠…

数据结构——单调队列

这篇博客我们来讨论一下单调队列的问题&#xff0c;其实和之前学的单调栈都是一种上通过改变操作来解决问题的一种数据结构 我们先来回忆一下单调栈的内容&#xff0c;这样方便将其和单调队列做区分 单调栈&#xff1a;(单调性从栈底到栈顶&#xff09; 1.单调栈是一种栈数据…

解决 Maven 部署中的 Artifact 覆盖问题:实战经验分享20241204

&#x1f6e0;️ 解决 Maven 部署中的 Artifact 覆盖问题&#xff1a;实战经验分享 &#x1f4cc; 引言 在软件开发过程中&#xff0c;持续集成和持续部署&#xff08;CI/CD&#xff09;是提高开发效率和代码质量的关键手段。Hudson 和 Maven 是两种广泛使用的工具&#xff0…

[go-redis]客户端的创建与配置说明

创建redis client 使用go-redis库进行创建redis客户端比较简单&#xff0c;只需要调用redis.NewClient接口创建一个客户端 redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379",Password: "",DB: 0, })NewClient接口只接收一个参数red…

Solving the Makefile Missing Separator Stop Error in VSCode

1. 打开 Makefile 并转换缩进 步骤 1: 在 VSCode 中打开 Makefile 打开 VSCode。使用文件浏览器或 Ctrl O&#xff08;在 Mac 上是 Cmd O&#xff09;打开你的 Makefile。 步骤 2: 打开命令面板 按 Ctrl Shift P&#xff08;在 Mac 上是 Cmd Shift P&#xff09;&…

交换机四大镜像(端口镜像、流镜像、VLAN镜像、MAC镜像)应用场景、配置实例及区别对比

在网络管理中&#xff0c;端口镜像、流镜像、VLAN镜像和MAC镜像都是用于监控和分析网络流量的重要技术。 端口镜像&#xff08;Port Mirroring&#xff09; 定义&#xff1a;端口镜像是将一个或多个源端口的流量复制到一个目标端口&#xff0c;以便于网络管理员能够监控和分析…

Unity数据持久化

二进制数据持久化的好处&#xff1a;安全、效率高、利于网络通信 文章目录 补充文件夹相关EditorResourcesSteammingAsset 序列化和反序列化序列化反序列化 二进制数据持久化转换为字节数据文件操作写入字节&#xff1a;读取字节安全关闭文件夹操作操作文件夹目录信息和文件信息…

【机器学习】机器学习的基本分类-监督学习-随机森林(Random Forest)

随机森林是一种基于集成学习&#xff08;Ensemble Learning&#xff09;思想的算法&#xff0c;由多个决策树构成。它通过结合多棵决策树的预测结果来提升模型的泛化能力和准确性&#xff0c;同时减少过拟合的风险。 1. 随机森林的核心思想 多样性&#xff1a; 随机森林通过引…

中国矿业大学《2024年868自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《中国矿业大学868自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2024年真题 Part1&#xff1a;2024年完整版真题 2024年真题

SQL SERVER 2016 AlwaysOn 无域集群+负载均衡搭建与简测

之前和很多群友聊天发现对2016的无域和负载均衡满心期待&#xff0c;毕竟可以简单搭建而且可以不适用第三方负载均衡器&#xff0c;SQL自己可以负载了。windows2016已经可以下载使用了&#xff0c;那么这回终于可以揭开令人憧憬向往的AlwaysOn2016 负载均衡集群的神秘面纱了。 …

生产看板到底在看什么?

说起生产看板&#xff0c;可能很多人脑海里冒出来的画面是&#xff1a;车间里一块挂在墙上的大板子&#xff0c;上面贴满了各式各样的卡片、表格&#xff0c;甚至还有几个闪闪发光的指示灯。但是&#xff0c;无论是精益生产方式代表——丰田&#xff0c;还是当下以“智能制造”…

数据链路层(四)---PPP协议的工作状态

1 PPP链路的初始化 通过前面几章的学习&#xff0c;我们学了了PPP协议帧的格式以及组成&#xff0c;那么对于使用PPP协议的链路是怎么初始化的呢&#xff1f; 当用户拨号上网接入到ISP后&#xff0c;就建立起了一条个人用户到ISP的物理链路。这时&#xff0c;用户向ISP发送一…