【Linux线程】

目录

  • 线程是操作系统的一个执行流
    • 并发编程
      • 进程并发的优劣
      • 基于线程的并发编程
      • Linux当中的线程
  • 线程的创建
    • 使用pthread_create
    • pthread_join对线程进行等待
    • pthread_exit和pthread_cancel
    • pthread_detach线程分离
    • 注意事项
  • 原生线程库,详谈Linux的线程
    • pthread库管理线程

线程是操作系统的一个执行流

在计算机操作系统中,线程是一个执行流,是被CPU调度的一个基本最小单位。是在一个执行的单一进程上下文的控制逻辑流。一个进程可以有多个线程,可以并发执行不同的任务。

并发编程

如果一个单核的计算机要在普通用户层面上看到有多个程序运行,就需要采用并发编程的技术来实现,可以通过进程来创建一个子进程或多个子进程来运行。操作状态给每个进程分配一定的时间片来让进程给CPU执行。
两个客户端和一个服务端之间的工作。
在这里插入图片描述

进程并发的优劣

对于父子进程来说,各自有一份独立的虚拟地址空间,当在内存的共享文件有其中的一个进程对数据进行修改的时候,操作系统就会对在内存的文件进行写实拷贝。这样就不会造成父子进程之间的数据混淆和覆盖的情况,这是并发进程的优点。但是对于进程来说,他们是独立的,所有进程间的交互就变得复杂,共享信息的时候需要进程间通信的机制。而且基于进程的并发设计的程序运行效率慢。

基于线程的并发编程

线程是一个执行在一个运行的进程之中的上下文的一个执行流。每个线程都是由进程创建,所以进程获取操作系统的资源,然后分配给线程,让线程可以被CPU进行调度。线程会共享进程的内存空间和其他资源。具体来说,同一个进程下的多个线程共享父进程的地址空间,包括代码段、数据段、堆和栈等内存区域。这意味着线程之间可以访问和操作这些共享的内存区域,包括全局变量、静态变量和动态分配的堆内存等。但每个线程都有之间独立的线程上下文来执行它的任务,由独立的栈和独立的寄存器中数据还有线程的id,错误码,优先级等。
在这里插入图片描述
每个执行流都可以对共享区的数据进行读写操作。
在这里插入图片描述
线程不同与进程,线程上下文要比进程小得多,占用的资源小。它在进程间执行,因为上下文下,所以线程间的切换要比进程间的切换要快的多,这就是多线程对比与多进程的优势。一个进程可以创建许多的进程,所以线程的创建先是主进程创建。这些线程都可以使用一些数据结构来对这些线程进行管理,把这些线程组织成为线程池。

Linux当中的线程

在linux当中,线程被称为轻量级进程,轻量级进程是基于进程实现,直接复用了进程的原理,但也可以实现和多线程的一样的功能。是一个多进程中存在多个轻量级的线程的多个执行流。
在这里插入图片描述

线程的创建

使用Posix的接口创建线程

使用pthread_create

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void (*start_routine) (void *), void *arg);
//创建成功返回值为0,创建失败放回错误码,错误信息被设置
#include <iostream>
#include <pthread.h>
#include <unistd.h>

void* thread(void *args)
{
    std::cout << "我是线程" << std::endl;
    return nullptr;
}

int main()
{
    pthread_t pid;
    pthread_create(&pid, nullptr, thread, nullptr);
    //使用pthread_create函数接口时我要把pid的地址传参到第一个参数
    //第二个参数设置为nullptr或NULL,让操作系统自动的帮我们找到物理地址的页框,让线程占用该空间
    //第三个参数设置为一个返回值为void*,参数也时void*的函数
    //第四个参数为第三个参数的函数的参数,该参数作为指针,如果想把多个参数传递到线程,可以把参数进行封装成一个对象,然后把该对象的地址转化为void*的类型进行传参,同样。返回值也可以返回一个多个参数的对象的指针。
    while(true)
    {
        std::cout << "我是主进程" << std::endl;
        sleep(2);
    }
    return 0;
}

在Linux中,线程的库并不属于操作系统的系统调用的库,而是第三方的库。所以我们编译的时候需要链接第三方的库。

g++ os_pthread.cpp -std=c++11 -pthread
//-pthread就是链接第三方的库
while :; do ps -al | head -1 && ps -al | grep threadname; sleep 1;done
//查看线程的id

在这里插入图片描述

pthread_join对线程进行等待

上述的代码,主进程实际上是一个不退出的进程。如果不让主进程一直的执行下去,主进程结束退出,如果线程要处理的任务的时间是100个单位,而主进程创建线程完成后进程就立即结束,则线程也跟着退出。因为线程是进程里的一个执行流,使用着进程的资源。进程退出了,资源被回收,线程就无法继续执行了,所以,我们可以使用接口pthread_join像父进程等待子进程那样,主线程等待线程。

int pthread_join(pthread_t thread, void **retval)
//retval可以获得线程退出时的信息
#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>

class ThreadReturn
{
public:
    ThreadReturn(pthread_t& pid,std::string threadmessage,int code)
        :_pid(pid),_threadmessage(threadmessage),_code(code)
    {}
public:
    pthread_t _pid;
    std::string _threadmessage;
    int _code;
};
void* thread(void *args)
{
    int cnt = 6;
    ThreadReturn *td = static_cast<ThreadReturn*>(args);
    std::cout << "pid: " << td->_pid << " threadmessage: " << td->_threadmessage << " code: " << td->_code << std::endl;
    while(cnt--)
    std::cout << "我是线程" << "cnt "<< cnt << std::endl;
    ThreadReturn* ret = new ThreadReturn(td->_pid, "线程退出", 11);
    pthread_exit(ret);
    //return ret;//也可以采用return来放回
}

int main()
{
    pthread_t pid;
    ThreadReturn* td = new ThreadReturn(pid, "线程开始", 10);
    pthread_create(&pid, nullptr, thread, td);
    //传了一个类的对象的指针
    void* ret = nullptr;
    pthread_join(pid, &ret);//对线程进行等待
    //使用一个类型为void*的变量来获取线程放回的信息
    ThreadReturn* ans = static_cast<ThreadReturn*>(ret);
    //用一个对应对象的指针变量来接收强转的ret
     std::cout << "pid: " << ans->_pid << " threadmessage: " << ans->_threadmessage << " code: " << ans->_code << std::endl;
    return 0;
}

pthread_exit和pthread_cancel

着两个函数接口都是让线程退出的函数接口。当线程调用pthread_exit时,线程会直接终止,如果时主线程调用,则会等待其他线程结束时再终止整个进程。如果使用pthread_cancel会对特点的id线程进行终止。

void pthread_exit(void *retval);
int pthread_cancel(pthread_t thread);
//取消线程成功返回值为0,pthread_join时的线程的返回值为-1//PTHREAD_CANCELED;

pthread_detach线程分离

int pthread_detach(pthread_t thread);
//分离成功放回0,失败错误码被设置

该函数最好在线程执行的函数体中进行编写。
任何时刻,线程都是课结合或者时分离的。结合的线程课被其他的线程所影响。只有当自己手动退出或主线程退出的时候,操作系统才回收资源。如果一个线程被脱离了,就不能被其它的线程所影响,分离的线程资源只有它自己结束的时候才能自动释放并入系统回收。就好比网络上的服务器,一般来说,一旦执行了,就不会退出。所以为了避免其他的线程出现的异常信号影响该线程,就要进行线程的分离,不受影响。线程一旦脱离了,pthread_join的返回值就是22。无意义。如果主线程不需要线程的返回值,就可以设置为分离。

void* thread(void *args)
{
	pthread_detach(pthread_self());
	//pthread_self函数获取自己的id
    int cnt = 6;
    ThreadReturn *td = static_cast<ThreadReturn*>(args);
    std::cout << "pid: " << td->_pid << " threadmessage: " << td->_threadmessage << " code: " << td->_code << std::endl;
    while(cnt--)
    std::cout << "我是线程" << "cnt "<< cnt << std::endl;
    ThreadReturn* ret = new ThreadReturn(td->_pid, "线程退出", 11);
    pthread_exit(ret);
    //return ret;//也可以采用return来放回
}

注意事项

如果线程触发了异常的信号,整个线程和主进程也会跟着异常退出,因为该主线程的其他线程和自己使用的是同一个信号集。

原生线程库,详谈Linux的线程

上述的函数接口都不是系统直接提供的接口,而是pthread原生库提供的接口。pthread原生库在Linux操作系统要有的。因为Linux的底层实现并没有线程的,而是使用轻量级进程(LWP)来让上层用户来作为线程使用的。而用户又不能很好的使用轻量级进程来实现多线程的功能,所以可以使用pthread原生库来对Linux系统的轻量级进程进行管理。上层就可以用用户级的线程了。
在这里插入图片描述
在这里插入图片描述

int clone(int (*fn)(void *), void *child_stack,int flags, void *arg, ...
                 /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

clone函数就是Linux创建进程的函数接口,通过对flags参数的不同来创建进程和轻量级进程,fork的底层就有clone,pthread线程库也有该函数接口来创建线程。fn就是pthread_create函数的第三个参数,函数调用方法的指针,arg就是第四个参数。clone会在地址空间申请一段空间做为线程的独立栈。默认地址空间的栈为主线程的栈空间。
在这里插入图片描述

pthread库管理线程

pthread库要作为动态库加载到内存当中,然后通过页表映射到进程地址空间。
在这里插入图片描述
pthread库加载到内存作为动态库,再通过页表映射到虚拟地址空间,所有的进程都公有该线程库,所以Linux的轻量级进程就会由pthread来进行管理。
在这里插入图片描述
在这里插入图片描述
同一个进程创建的线程pid都是一样的,但是LWP不一样。

在这里插入图片描述

所以得出结论,线程id是虚拟地址空间的地址。可以使用%lx来对线程id进行观察。

printf("tid address: %lx\n",tid);

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

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

相关文章

云端部署Stirling PDF:构建个人App的API调用指南(附Python源码)

今天发现一个Github的开源项目&#xff0c;Stirling PDF&#xff0c;项目地址如下&#xff1a;https://gitcode.com/Stirling-Tools/Stirling-PDFhttps://gitcode.com/Stirling-Tools/Stirling-PDF?utm_sourceartical_gitcode目前CSDN上已经有好几个up主都介绍了这个项目&…

cocos=》 预乘、混合(黑边、白色)

简介 预乘&#xff0c;指的是在数据提交给GPU之前&#xff0c;就对纹理的RGB分量与alpha值进行计算。 预乘计算 结果颜色 源颜色值 目标颜色值 * (1 - 源 alpha 值) result source.RGB dest.RGB * (1 - source.A); 对应的颜色混合函数设置为 gl.blendFunc(gl.ONE, gl.…

英语复习之英语形近词总结

最近在练习英语口语&#xff0c;有很好的练习场景&#xff0c;和数字人对练&#xff0c;还能纠错&#xff0c;不过开口的基础需要单词量的支撑以及语法的熟悉&#xff0c;因为英语的语法太简单了&#xff0c;没啥需要复习和注意的&#xff0c;音标发音的问题也可以后期再纠正&a…

Angular进阶-NVM管理Node.js实现不同版本Angular环境切换

一、NVM介绍 1. NVM简介 Node Version Manager&#xff08;NVM&#xff09;是一个用于管理多个Node.js版本的工具。它允许用户在同一台机器上安装和使用多个Node.js版本&#xff0c;非常适合需要同时进行多个项目的开发者。NVM是开源的&#xff0c;支持MacOS、Windows和Linux…

wechat_OCR项目打包以及如何使用

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…

医学图像处理:nii格式转换(3D切片为2D)

目录 NIFTI文件结构 读取NII文件 ITK-SNAP安装 使用方法 NII转PNG NIFTI文件结构 NIFTI 格式&#xff0c;是一种用于存储和交换医学成像数据的文件格式&#xff0c;特别适用于神经影像学领域。NIFTI文件通常有两个扩展名&#xff1a;.nii&#xff08;用于图像数据&#xf…

42.WEB渗透测试-信息收集-域名、指纹收集(4)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;41.WEB渗透测试-信息收集-域名、指纹收集&#xff08;3&#xff09; 关于单域名收集内容…

基于JSP的酒店客房管理系统(二)

目录 第二章 相关技术介绍 2.1 Jsp的简介 2.2 sql server 2005 的简介 第三章 系统的分析与设计 3.1 系统需求分析 1&#xff0e;理解需求 2&#xff0e;需求分析 3.2开发及运行环境 3.3功能模块的设计 3.3.1 设计目标 3.3.2 客房管理系统前台的设计 3.3.3 操作员管…

一种算法分类方式及其应用

在计算机科学领域&#xff0c;算法是解决问题的有效方法&#xff0c;而对算法进行分类有助于理解它们的特性、优劣以及在不同场景下的应用。常见的算法分类方法&#xff0c;包括按设计思想、问题类型、数据结构和应用领域等&#xff0c;每一类算法会对应有其典型和实际应用。 算…

大数据BI可视化(Echarts组件)项目开发-熟悉交互API5.0

全局echarts对象 init初始化 registerTheme注册主题 var mCharts echarts.init(document.querySelector("div"), itcast)registerMap地图图表 connect 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8&qu…

javaFor循环-打印九九乘法表

虽然所有循环结构都可以用while或者do...while表示&#xff0c;但java提供了另一种循环语句--for循环&#xff0c;使一些循环结构变得简单。for循环语句是支持迭代的一种通用结构&#xff0c;是最有效&#xff0c;最灵活的循环结构。 先写第一列&#xff1a; 运行结果&#xf…

什么是开发者门户?最佳实践及示例

原文链接&#xff1a;https://document360.com/blog/api-developer-portal-examples 开发者门户是什么&#xff1f; DevPortal 奖的主要赞助商 Provonix 对开发者门户的定义如下&#xff1a; “开发者门户&#xff08;通常缩写为 DevPortal&#xff09;是一组 API、SDK 或其他…

【电机控制】七段式SVPWM扇区、矢量作用时间计算——对比simplefoc与Ti例程

【电机控制】七段式SVPWM扇区、矢量作用时间计算——对比simplefoc与Ti例程 文章目录 前言一、simplefoc——通过角度找扇区1.通过角度找扇区理论1.通过角度找扇区2.矢量作用时间计算3.矢量切换时间计算——七段式 2.simplefoc代码3.解读simplefoc代码1.通过角度找扇区2.矢量作…

关于YOLO8学习(四)模型转换为ncnn

前文 关于YOLO8学习(一)环境搭建,官方检测模型部署到手机 关于YOLO8学习(二)数据集收集,处理 关于YOLO8学习(三)训练自定义的数据集 简介 本文将会讲解: (1)如何通过PyCharm,进行pt模型的转换,最后输出一个适合手机端使用的模型 开发环境 win10、python 3.11…

[ARM系列]coresight(一)

原文链接 目的&#xff1a;对复杂SOC实现debug和trace的架构 典型环境 包含&#xff1a;2个ARM core&#xff0c;一个DSP&#xff0c;众多coresight组件 coresight组件实现对core、DSP的debug和trace功能 环境中包含3个通路 trace通路&#xff1a;将core和DSP内部信息输出到…

【机器学习-21】集成学习---Bagging之随机森林(RF)

【机器学习】集成学习---Bagging之随机森林&#xff08;RF&#xff09; 一、引言1. 简要介绍集成学习的概念及其在机器学习领域的重要性。2. 引出随机森林作为Bagging算法的一个典型应用。 二、随机森林原理1. Bagging算法的基本思想2. 随机森林的构造3. 随机森林的工作机制 三…

【C++】学习笔记——vector_3

文章目录 七、vector3. vector的模拟实现4. vector实现代码整合 未完待续 七、vector 3. vector的模拟实现 上篇文章我们讲解了非常 玄幻 的拷贝构造函数&#xff0c;同样的方法&#xff0c;我们也能用这种方法来实现 赋值重载函数 。 void swap(vector<T>& v) {s…

【Linux 网络】网络基础(一)(局域网、广域网、网络协议、TCP/IP结构模型、网络传输、封装和分用)-- 详解

一、计算机网络的发展背景 1、网络的定义 网络是指将多个计算机或设备通过通信线路、传输协议和网络设备连接起来&#xff0c;形成一个相互通信和共享资源的系统。 &#xff08;1&#xff09; 独立模式 独立模式 &#xff1a; 计算机之间相互独立。 &#xff08;2&#xff09;…

C语言二分查找的区间问题

概念 什么是二分查找呢&#xff1f; 二分查找&#xff1a;在有序数组中查找某一特定元素的搜索算法。 二分查找又称折半查找&#xff0c;通过将数组折半&#xff0c;用中间值和查找值作比较&#xff0c;多次使用&#xff0c;直到找到要查找的值。 注意:二分查找的前提是&#…

【xxl-job | 第二篇】Windows源码安装xxl-job

文章目录 2.Windows源码安装xxl-Job2.1拉取源码2.2IDEA导入2.3初始数据库数据2.4修改properties配置2.5启动admin并进入任务管理后台2.6jar包运行&#xff08;部署到Linux服务器上&#xff09;2.6.1打包2.6.2在xxl-job-admin打开jar包目录2.6.3cmd运行jar包 2.Windows源码安装x…