Linux之多线程

目录

一、进程与线程

1.1 进程的概念

1.2 线程的概念

1.3 线程的优点

1.4 线程的缺点

1.5 线程异常

1.6 线程用途

二、线程控制

2.1 POSIX线程库

2.2 创建一个新的线程

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

2.4 线程终止

2.5 线程等待

2.6 线程分离


一、进程与线程

在学习多线程之前,我们首先要搞清楚进程与线程的区别。

1.1 进程的概念

        进程是操作系统进行资源分配的基本单位。每个进程都有一个唯一的标识符(PID),用于操作系统在管理和调度进程时进行标识和区分。操作系统可以根据一定的调度算法来分配CPU时间片给不同的进程,从而实现多任务并发执行。在Linux中一个进程的创建实际上伴随着进程控制块(PCB或task_struct),进程地址空间以及页表的创建。

1.2 线程的概念

        在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”。线程是操作系统进行任务调度的基本单位

        要知道,Linux并不存在真正意义上的线程。线程比进程往往是多对一的,即一切进程至少都有一个执行线程:

        线程在进程内部运行,本质是在进程地址空间内运行的 ,如果Linux实现真的线程,那么就需要对这些线程进行管理。比如说创建线程、终止线程、调度线程、切换线程、给线程分配资源、释放资源以及回收资源等等,搞一套与进程类似的线程管理模块,整个难度就比较大。线程并没有独立的虚拟地址空间,只是在进程虚拟地址空间中拥有相对独立的一块空间:

      

1.3 线程的优点

  • 线程的粒度小于进程,不论是创建、切换还是占用资源线程要付出的代价都更少,因此通常多线程比多进程并发性更高
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务。
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

1.4 线程的缺点

  • 性能损失

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

  • 健壮性降低

        编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。比如:进程之间是相互独立的,我们打开各种软件,一个软件的崩溃并不会影响其他软件,变相的也就增加了进程的健壮性,而线程就不同了,因为大部分资源都是共享的,一个线程的崩溃就会导致其他所有线程崩溃,进而导致整个进程崩溃;

  • 缺乏访问控制

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

  • 编程难度提高

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

1.5 线程异常

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃。
  • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。

1.6 线程用途

  • 合理的使用多线程,能提高CPU密集型程序的执行效率。
  • 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)。

二、线程控制

2.1 POSIX线程库

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

2.2 创建一个新的线程

参数:

  • thread:返回线程ID
  • attr:设置线程的属性,attr为NULL表示使用默认属性
  • start_routine:是个函数地址,线程启动后要执行的函数
  • arg:传给线程启动函数的参数
  • 返回值:成功返回0;失败返回错误码

错误检查:

  • 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
  • pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回。
  • pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误, 建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小。

下面尝试使用一段代码创建线程:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
void* thread_func(void* args)
{
    while(true)
    {
        cout << "new thread running..." << endl;
        sleep(1);
    }
}
int main()
{
    pthread_t t;
    pthread_create(&t,nullptr,thread_func,nullptr);
    while(true)
    {
        cout<<"main thread is running..."<<endl;
        sleep(1);
    }
    return 0;
}

运行结果: 

此时我们可以用一个Linux命令查找进程并输出相关信息:ps -aL 

        可以看到它们的PID是相同的,因为它们属于同一个进程,而LWP指的是轻量级进程ID(Linux中没有线程,而是叫做轻量级进程)。

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

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

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

2.4 线程终止

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程

void pthread_exit(void *retval);

  • 功能:线程终止
  • 参数:retva表示线程退出时的退出码信息
  • 返回值:无返回值,跟进程一样结束时无法返回自身。
intpthread_cancel(pthread_t thread);
  • 功能:取消一个执行中的线程
  • 参数:线程ID
  • 返回值:成功返回0,失败返回错误码

2.5 线程等待

为什么需要线程等待? 

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

int pthread_ join(pthread_t thread,void **value_ptr);

  • 功能:等待线程结束
  • 参数:thread表示线程ID,value_ptr 指向一个指针,后者指向线程的返回值。 
  • 返回值:成功返回0,失败返回错误码。      

调用该函数的线程将挂起等待,直到id为thread的线程终止,线程以不同的方法终止,通过pthread_join得到的终止状态是不同的:

  • 如果thread线程通过return返回,retval 所指向的单元里存放的是thread线程函数的返回值。
  • 如果thread线程被别的线程调用 pthread_cancel 异常终止掉,retval 所指向的单元里存放的是常数PTHREAD_CANCELED,就是 -1。
  • 如果thread线程是自己调用 pthread_exit 终止的,retval 所指向的单元存放的是传给 pthread_exit 的参数。
  • 如果对thread线程的终止状态不感兴趣,可以传NULL给 retval 参数。

2.6 线程分离

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
  • int pthread_detach(pthread_t thread);
可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:
  • pthread_detach(pthread_self());

joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

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

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

相关文章

HDFS的超级用户

一. 解释原因 HDFS(Hadoop Distributed File System)和linux文件系统管理一样&#xff0c;也是存在权限控制的。 但是很不一样的是&#xff0c; 在Linux文件系统中&#xff0c;超级用户Superuser是root而在HDFS中&#xff0c;超级用户Superuser是启动了namenode的用户&#x…

4核16g配置咋样?还不错,阿里云4核16G服务器26元1个月

4核16g配置咋样&#xff1f;还不错&#xff0c;阿里云4核16G服务器26元1个月&#xff0c;阿里云4核16G服务器配置优惠价格ECS云服务器经济型e实例26元1个月、149元半年、79元3个月&#xff0c;4核16G通用算力u1服务器、通用型g7、通用型g8i、AMD通用型g8a、性能增强通用型g8ae、…

html从零开始6:关系选择器、css盒子模型、弹性盒子模型【搬代码】

关系选择器 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width,…

Linux日志管理服务 rsyslogd

文章目录 1. 基本介绍2. 系统常用的日志3. 日志管理服务 rsyslogd 1. 基本介绍 日志文件是重要的系统信息文件&#xff0c;其中记录了许多重要的系统事件&#xff0c;包括用户的登录信息、系统的启动信息、系统的安全信息、邮件相关信息、各种服务相关信息等。日志对于安全来说…

QQ群微信群强制加群/随机小姐姐视频分享打赏裂变PHP源码

很多人再找qq群或者微信分享小姐姐视频打赏的裂变源码&#xff0c;这次就分享出来&#xff01; 下载地址QQ群微信群强制加群.zip官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘

中国电子学会2023年12月份青少年软件编程Scratch图形化等级考试试卷二级真题(含答案)

2023-12 Scratch二级真题 分数&#xff1a;100 题数&#xff1a;37 测试时长&#xff1a;60min 一、单选题(共25题&#xff0c;共50分) 1.在制作推箱子游戏时&#xff0c;地图是用数字形式储存在电脑里的&#xff0c;下图是一个推箱子地图&#xff0c;地图表示如下&#x…

QGIS教程 加载shape数据 矢量数据(批量)

一. 前言 本篇文章主要介绍QGIS的使用&#xff0c;包括如何加载矢量shape数据、查看数据属性、如何加载txt属性数据、怎么用脚本批量加载矢量数据等内容。 如果想了解QGIS&#xff0c;可以参考博文&#xff1a; QGIS基本介绍 如果想了解shape数据格式详情&#xff0c;可以参考…

bpmn-js 事件总线处理

bpmn-js中使用EventBus作为事件的处理句柄&#xff0c;EventBus的使用和我们常规使用的事件总线没啥大的区别&#xff0c;其源码位于&#xff1a;/diagram-js/lib/core/EventBus.js &#xff08;bpmn-js使用diagram-js实现流程图的web端绘制呈现工具&#xff09;。 EventBus使用…

权限提升:利用Linux错配提权

目录 Linux权限基础 Linux用户权限 Linux文件权限 特殊的Linux文件权限 Linux本机信息收集 Linux错配提权 crontab计划任务提权 SUID提权 Linux权限基础 Linux用户权限 在Linux中&#xff0c;根据权限的不同&#xff0c;大致可以分为三种&#xff1a;超级用户&#x…

steam游戏搬砖项目靠谱吗?有没有风险?

作为一款fps射击游戏&#xff0c;csgo在近几年可谓是火出圈&#xff0c;作为一款全球竞技游戏&#xff0c;深受玩家喜爱追捧&#xff0c;玩家追求的就是公平公正&#xff0c;各凭本事&#xff0c;像其他游戏可能还会有皮肤等装备属性加成&#xff0c;在csgo里面是不存在的。 纯…

【Java程序设计】【C00257】基于Springboot的校园二手书交易平台(有论文)

基于Springboot的校园二手书交易平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的乐校园二手书交易管理系统 本系统分为系统功能模块、管理员功能模块、卖家用户功能模块以及用户功能模块。 系统功能模块&…

semantic-kernel 国内代理的配置(详细教程)

1. semantic-kernel概述 Semantic Kernel 是微软提供的一个工具,旨在帮助开发者快速、轻松地将最新的大型语言模型(LLM)技术集成到应用程序中。这意味着通过使用Semantic Kernel,开发者可以在他们的应用中加入先进的人工智能和自然语言理解能力,从而增强应用的功能性。 …

在python中JSON数据格式的使用

什么是JSON&#xff1f; JSON是一种数据格式&#xff0c;由美国程序设计师DouglasCrockford创建的&#xff0c;JSON全名是JavaScript Object Notation,由JSON英文全文字义我们可以推敲JSON的缘由&#xff0c;最初是为JavaScript开发的。这种数据格式由于简单好用被大量应用在We…

Linux第57步_“linux系统镜像”和“根文件系统”重新打包小结

实时总结是很重要的&#xff0c;时间久了&#xff0c;可能会遗忘。下面是“linux系统镜像”和“根文件系统”重新打包小结。 1、linux系统镜像重新打包 在第1次完成linux系统镜像打包后&#xff0c;再重新打包就很容易了 将“bootfs”重新打包 准备打包文件: 输入“cd /ho…

顺序表、链表(ArrayList、LinkedList)

目录 前言&#xff1a; 顺序表&#xff08;ArrayList&#xff09;&#xff1a; 顺序表的原理&#xff1a; ArrayList源码&#xff1a; 的含义&#xff1a;​编辑 ArrayList的相关方法&#xff1a;​编辑 向上转型List&#xff1a; 练习题&#xff08;杨辉三角&#x…

数模.传染病模型plus

一、SIS模型 二、SIR模型 三、SIRS模型 四、SEIR模型

C++类和对象-C++运算符重载->加号运算符重载、左移运算符重载、递增运算符重载、赋值运算符重载、关系运算符重载、函数调用运算符重载

#include<iostream> using namespace std; //加号运算符重载 class Person { public: Person() {}; Person(int a, int b) { this->m_A a; this->m_B b; } //1.成员函数实现 号运算符重载 Person operator(const Per…

【有哪些值得计算机专业学生加入的国企?】

&#x1f680; 编辑&#xff1a;“码上有前” &#x1f680;作者丨重庆搬砖喵知乎 &#x1f680; 文章简介 &#xff1a;计算机专业未来出路 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; &#x1f680;来源&#xff1a;https://www.zhihu.com/qu…

PHP+vue+mysql校园学生社团管理系统574cc

运行环境:phpstudy/wamp/xammp等 开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat/phpmyadmin 前台功能&#xff1a; 首页&#xff1a;展示社团信息和活动…

相机图像质量研究(18)常见问题总结:CMOS期间对成像的影响--CFA

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…