Linux - 线程基础

文章目录

    • 1.什么是线程
    • 2.线程vs进程
    • 3.线程调度
    • 4.线程控制
      • 4.1 POSIX线程库
      • 4.2创建线程
      • 4.3线程终止
      • 4.4线程等待
      • 4.5线程分离
    • 5、线程封装

1.什么是线程

  1. 在Linux操作系统中,线程是进程内部的一个执行流。
  2. 在Linux操作系统下,执行流统称为轻量级进程(也是task_struct作为数据结构,简称LWP)(也就是说在Linux下线程是通过LWP模拟实现的)。

2.线程vs进程

  1. 进程是承当资源分配的基本实体,线程是os调度的基本单位。
  2. 线程共享进程的地址空间(即共享数据)。
    (1)文件描述符 (2)信号处理方式 (3)当前工作目录 (4)用户id和组id
  3. 线程也有自己独立的一些数据
    (1)栈 (2)寄存器 (3)线程ID (4)调度优先级 (5)errno (6)信号屏蔽字

3.线程调度

  1. 如果线程所属的进程不被调度,那么该进程内的所有线程(包括主线程和其他任何线程)通常也不会被调度执行。
  2. 线程调度也是受到优先级、调度策略等影响。
  3. 线程调度的时机
    时间片用完:操作系统为每个线程分配一个时间片,当线程的时间片用完时,操作系统会将调度权交给其他线程。
    阻塞操作:当线程执行了一个阻塞操作,例如等待输入、等待磁盘读写等,操作系统会将线程切换到阻塞状态,同时调度其他可运行线程。
    优先级调度:线程的优先级可能会影响调度。当高优先级的线程就绪时,操作系统可能会将其切换到运行状态,同时调度低优先级的线程。
    睡眠和唤醒:线程可以通过调用sleep()或wait()等方法主动让出CPU,进入睡眠状态。当休眠时间到期或条件满足时,线程会被唤醒并重新调度。
    中断处理:当线程遇到硬件中断时,操作系统可能会中断当前线程的执行,切换到中断处理程序的上下文。

4.线程控制

4.1 POSIX线程库

  1. 与线程有关的函数构成了⼀个完整的系列,绝⼤多数函数的名字都是以“pthread_”打头的。
  2. 要使⽤这些函数库,要通过引⼊头⽂ <pthread.h>。
  3. 链接这些线程函数库时要使⽤编译器命令的“-lpthread”选项。

4.2创建线程

函数原型

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);

参数

  1. thread:指向 pthread_t 类型的指针。
    用途:用于存储新创建的线程的标识符(ID)。
    调用成功后,这个参数指向的位置会被赋予新线程的ID。
    注意:在使用这个线程ID之前,不需要对其进行任何初始化。
  2. attr:指向 pthread_attr_t 类型的指针,该类型是一个结构体,用于指定线程的属性。
    用途:允许调用者设置新线程的属性,如是否分离(detach)、堆栈大小、调度策略和优先级等。
    默认值:如果设置为 NULL,则使用默认属性创建线程。
  3. vstart_routine:函数指针,指向一个返回 void类型并接受一个 void 类型参数的函数。
    用途:这是新线程将要执行的函数的地址。该函数称为线程的启动例程(start
    routine)或线程函数。 参数:该函数接受一个 void* 类型的参数(arg),允许调用者向线程函数传递任意数据。
    返回值:线程函数的返回值将被传递给 pthread_exit 函数(如果线程调用它)或作为线程的退出状态(如果线程直接返回)。
  4. arg :void* 类型的指针。
    用途:传递给线程函数的参数。它允许调用者向线程函数传递任意数据。
    注意:在实际使用中,通常需要将这个参数转换为适当的类型,以便在线程函数内部使用。

返回值

  1. 成功:返回 0。
  2. 失败:返回一个非零的错误码,这些错误码通常定义在 <pthread.h> 头文件中,并且可以通过 strerror 或 perror 函数转换为可读的错误信息。

功能

用于创建新线程的函数。

例子
创建一个线程并执行函数

#include<iostream>
#include<pthread.h>
#include <unistd.h>//3.线程会执行到这里
void * func(void *arg)
{while(true){printf("%s\n",(char*)arg);sleep(1);}
}int main()
{//1.创建线程pthread_t pt;pthread_create(&pt,nullptr,func,(void*)"我是新线程");//2.主线程while(true){printf("我是一个主线程\n");sleep(1);}return 0;
}

在这里插入图片描述
现象:主线程和新线程都在执行各自的任务。

查看LWP

ps -aL | head -1 && ps -aL | grep test

在这里插入图片描述
现象:pid都相同说明是同一个进程,lwp区分不同线程。

补充:

在系统内核中只认识LWP,而我们用户需要使用线程就需要用thread标识符去操作线程,这是因为 POSIX线程库给我们用户进行了封装这类似于文件描述符bf和FILE*的关系。

4.3线程终止

4.3.1 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit(结束进程)。

4.3.2pthread_ exit
功能

结束线程。

函数原型

void pthread_exit(void *retval);

参数

retval: 是一个指向返回值的指针,这个返回值可以被其他线程通过 pthread_join 函数(线程等待的函数)获取。如果线程没有设置返回值(即不关心其退出状态),可以传递 NULL 作为 retval 的参数。

4.3.3pthread_ cancel
功能

请求取消指定线程的函数。当一个线程调用 pthread_cancel 并向另一个线程发送取消请求时,被请求的线程可以选择在合适的时机终止执行。

函数原型

int pthread_cancel(pthread_t thread);

参数

thread: 线程标识符。

返回值

  1. 成功时返回 0。
  2. 失败时返回一个非零的错误码。

4.4线程等待

4.4.1为什么进行等待呢

  1. 释放线程的空间(有点像进程等待)。
  2. 获取线程的退出状态。

4.4.2pthread_join
功能

用于等待指定线程终止的函数。调用 pthread_join 的线程(通常是主线程或另一个线程)会被阻塞,直到被等待的线程结束执行或取消,并取到被等待线程的返回值或处理其终止后的资源清理。

函数原型

int pthread_join(pthread_t thread, void **retval);

参数

  1. thread : 线程标识符。
  2. 被等待线程的返回值。

返回值

  1. 成功时返回 0。
  2. 失败时返回一个非零的错误码,例如 ESRCH(无此线程)、EINVAL(线程不是可连接的)或 EDEADLK(检测到死锁)。
  3. 死锁是指两个或两个以上的进程(或线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

具体解释

  1. 如果thread线程通过return返回,value_ ptr所指向的单元⾥存放的是thread线程函数的返回值。
  2. 如果thread线程被别的线程调⽤pthread_ cancel异常终掉,value_ ptr所指向的单元⾥存放的是常 数PTHREAD_ CANCELED。
  3. 如果thread线程是⾃⼰调⽤pthread_exit终⽌的,value_ptr所指向的单元存放的是传给 pthread_exit的参数。
  4. 如果对thread线程的终⽌状态不感兴趣,可以传NULL给value_ ptr参数。

例子
创建新线程,再使用pthread_ exit结束线程,再用主线程进行等待。

#include<iostream>
#include<pthread.h>
#include <unistd.h>
#include<stdio.h>//3.线程会执行到这里
void * func(void *arg)
{//4.结束自己char * ret = new char[50];ret[0] = 'a';ret[1] = 'b';ret[2] = '\0';pthread_exit((void*)ret);return nullptr;
}int main()
{//1.创建线程pthread_t pt;pthread_create(&pt,nullptr,func,(void*)"我是新线程");//3.主线程进程等待void *ret;int n = pthread_join(pt,&ret);(void)n;    //防止报警告// 5.输出返回值printf("%s\n",(char*)(ret));return 0;
}

在这里插入图片描述

4.5线程分离

4.5.1为什么要进行线程分离

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

4.5.2pthread_detach
功能

用于将指定的线程与调用线程(通常是主线程)分离

函数原型

int pthread_detach(pthread_t thread);

参数

thread:线程标识符。

返回值

  1. 成功时,返回 0。
  2. 失败时,返回错误码。常见的错误码包括:
    ESRCH: 指定的线程标识符无效或不存在。
    EINVAL:线程已经是分离状态或已经是 joinable 状态但已经终止。

补充:

  1. 可以有自己分离也可以由其他线程分离。
  2. pthread_self():或者自己的线程标识符。
  3. 等待和分离是冲突的。

5、线程封装

使用面向对象的方式对线程进行封装。

#ifndef _THREAD_HPP__
#define _THREAD_HPP__#include <iostream>
#include <string>
#include <pthread.h>
#include <functional>
#include <sys/types.h>
#include <unistd.h>namespace ThreadModule
{//函数包装using func_t = std::function<void()>;//线程个数static int number = 1;//状态enum class TSTATUS{NEW,RUNNING,STOP};class Thread{private://执行线程函数static void *Routine(void *args){//强制类型转换Thread* thread = (Thread*)args;//调用任务thread->_func();return nullptr;}public:Thread(func_t func) : _func(func), _status(TSTATUS::NEW), _joinable(false){_name = "Thread-" + std::to_string(number);_pid = getpid();}//创建线程bool Start(){if (_status != TSTATUS::RUNNING) // 保证线程处于非运行状态{int n = pthread_create(&_tid, nullptr, Routine, (void *)this); // 创建线程if (n != 0){return false;}_status = TSTATUS::RUNNING; // 更新状态return true;}return false;}//取消线程bool Stop(){if(_status == TSTATUS::RUNNING) //保证线程处于运行状态{int n = pthread_cancel(_tid);   //取消线程if(n != 0){return false;}_status = TSTATUS::STOP; // 更新状态return true;}return false;}//等待线程bool Join(){//保证线程不处于分离状态且处于运行状态if(!_joinable && _status == TSTATUS::RUNNING)  {   //等待线程,默认不关心线程状态int n = pthread_join(_tid,nullptr);if(n != 0){return false;}_status = TSTATUS::STOP; // 更新状态return true;}return false;}//线程分离void Detach(){//保证线程不处于分离状态且处于运行状态if(!_joinable && _status == TSTATUS::RUNNING)  {int n = pthread_detach(_tid);    //进行线程分离if(n != 0){return ;}std::cout<<"完成线程分离\n"<<std::endl;_joinable = true; //更新分离状态}}bool IsJoinable(){return _joinable;}std::string Name(){return _name;}~Thread() {}private:std::string _name; // 线程namepthread_t _tid;    // 线程idpid_t _pid;        // 进程idbool _joinable;    // 是否是分离的,默认不是func_t _func;      // 线程执行的任务TSTATUS _status;   // 线程状态};
}#endif

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

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

相关文章

5、AI测试辅助-生成测试用例思维导图

AI测试辅助-生成测试用例思维导图 创建测试用例两种方式1、Plantuml思维导图版本 (不推荐&#xff09;2、Markdown思维导图版本&#xff08;推荐&#xff09; 创建测试用例两种方式 完整的测试用例通常需要包含以下的元素&#xff1a; 1、测试模块 2、测试标题 3、前置条件 4、…

PTA编程题:N个数求和

问题描述 思路分析 问题分解 输入处理&#xff1a;将分数拆解为分子和分母&#xff0c;存储并处理。 分数相加规则&#xff1a; 即分子相加、分母相乘。 结果化简&#xff1a;求分数的最大公因数&#xff08;GCD&#xff09;&#xff0c;将其约分至最简形式。 带分数处理&…

Selenium + 数据驱动测试:从入门到实战!

引言 在软件测试中&#xff0c;测试数据的多样性和灵活性对测试覆盖率至关重要。而数据驱动测试&#xff08;Data-Driven Testing&#xff09;通过将测试逻辑与数据分离&#xff0c;极大地提高了测试用例的可维护性和可扩展性。本文将结合Selenium这一流行的测试工具&#xff0…

RK3568平台(中断篇)ARM中断流程

一.ARM 处理器程序运行的过程 ARM芯片属于精简指令集计算机 (RISC: Reduced Instruction Set Computing),它所用的指令比较简单,有如下特点: ① 对内存只有读、写指令 ② 对于数据的运算是在CPU内部实现 ③ 使用RISC指令的CPU复杂度小一点,易于设计 比如对于 a=a+b 这…

视频修复技术和实时在线处理

什么是视频修复&#xff1f; 视频修复技术的目标是填补视频中的缺失部分&#xff0c;使视频内容连贯合理。这项技术在对象移除、视频修复和视频补全等领域有着广泛的应用。传统方法通常需要处理整个视频&#xff0c;导致处理速度慢&#xff0c;难以满足实时处理的需求。 技术发…

推荐一款专业电脑护眼工具:CareUEyes Pro

CareUEyes Pro是一款非常好用的专业电脑护眼工具&#xff0c;软件小巧&#xff0c;界面简单&#xff0c;它可以自动过滤电脑屏幕的蓝光&#xff0c;让屏幕显示更加的不伤眼&#xff0c;更加舒适&#xff0c;有效保护你的眼睛&#xff0c;可以自定义调节屏幕的色调&#xff0c;从…

Element UI 组件库详解【Vue】

文章目录 一、引言二、安装并使用1. 安装2. 使用 三、常见组件说明1. 基础组件2. 布局组件3. 布局容器4. 选择框组件5. 输入框组件6. 下拉框组件7. 日期选择器8. 上传组件9. 表单组件10. 警告组件11. 提示组件12. 表格组件 一、引言 官方网站&#xff0c;element.eleme.cn El…

相机触发模式

参考自&#xff1a;相机触发模式_硬触发和软触发的区别-CSDN博客 一、图像采集模式分类 相机的图像采集模式分为内触发模式与外触发模式。其中内触发模式包含连续采集、单帧采集两种形式&#xff1b;外触发模式包含软件外触发、硬件外触发。本文以海康相机的软件平台作介绍&a…

脚手架vue-cli,webpack模板

先安装node.js&#xff0c;它是服务器端&#xff0c;用于给页面提供服务。前端学习不需要会node.js&#xff0c;只需要学会node.js衍生出来的npm命令即可。 npm 是node.js的一个工具&#xff0c;作用是进行包管理&#xff0c;npm是node.js的包管理器。 接着安装脚手架&#xff…

Stable Diffusion核心网络结构——CLIP Text Encoder

&#x1f33a;系列文章推荐&#x1f33a; 扩散模型系列文章正在持续的更新&#xff0c;更新节奏如下&#xff0c;先更新SD模型讲解&#xff0c;再更新相关的微调方法文章&#xff0c;敬请期待&#xff01;&#xff01;&#xff01;&#xff08;本文及其之前的文章均已更新&…

ggplot2 分面图等添加注释文字,相加哪里加哪里: 自定义函数 AddText()

如果分面图上还想再添加文字&#xff0c;只能使用底层的grid包了。 函数定义 # Add text to ggplot2 figures # # param label text you want to put on figure # param x position x, left is 0, right 1 # param y position y, bottom is 0, up 1 # param color text color…

ubuntu中使用ffmpeg和nginx推流rtmp视频

最近在测试ffmpeg推流rtmp视频&#xff0c;单独安装ffmpeg是无法完成推流的&#xff0c;需要一个流媒体服务器&#xff0c;常用nginx&#xff0c;可以直接在ubuntu虚拟机里面测试一下。 测试过程不涉及编译ffmpeg和nginx&#xff0c;仅使用基本功能&#xff1a; 1 安装ffmpeg …

解决upload上传之后,再上传没有效果

解决upload上传之后&#xff0c;再上传没有效果 注释&#xff1a;这是第二次上传&#xff0c;两次网络请求都是第一次上传的&#xff0c;这次上传没有网络请求 原因&#xff1a;在我的代码里我限制了上传数量为1&#xff0c;然后上传成功后&#xff0c;上传列表没有清空&#…

NVR接入录像回放平台EasyCVR视频融合平台加油站监控应用场景与实际功能

在现代社会中&#xff0c;加油站作为重要的能源供应点&#xff0c;面临着安全监管与风险管理的双重挑战。为应对这些问题&#xff0c;安防监控平台EasyCVR推出了一套全面的加油站监控方案。该方案结合了智能分析网关V4的先进识别技术和EasyCVR视频监控平台的强大监控功能&#…

基于web的音乐网站(Java+SpringBoot+Mysql)

目录 1系统概述 1.1 研究背景 1.2研究目的 1.3系统设计思想 2相关技术 2.1 MYSQL数据库 2.2 B/S结构 2.3 Spring Boot框架简介 3系统分析 3.1可行性分析 3.1.1技术可行性 3.1.2经济可行性 3.1.3操作可行性 3.2系统性能分析 3.2.1 系统安全性 3.2.2 数据完整性 …

中间件--laravel进阶篇

laravel版本11.31,这中间件只有3种,分别是全局中间件,路由中间件,控制器中间件。相比thinkphp8,少了一个应用中间件。 一、创建中间件 laravel创建中间件可以使用命令的方式创建,非常方便。比如php artisan make:middleware EnsureTokenIsValid。EnsureTokenIsValid是中间…

杰发科技AC7840——EEP中RAM的配置

sample和手册中示例代码的sram区地址定义不一样 这个在RAM中使用没有限制&#xff0c;根据这个表格留下足够空间即可 比如需要4096字节的eep空间&#xff0c;可以把RAM的地址改成E000&#xff0c;即E000-EFFF&#xff0c;共4096bytes即可。

实验室管理平台:Spring Boot技术构建

3系统分析 3.1可行性分析 通过对本实验室管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本实验室管理系统采用SSM框架&#xff0c;JAVA作为开发语言&a…

ThinkPHP8使用workerman

应用场景说明&#xff1a;通过建立通信&#xff0c;不同用户进行消息推送或数据更新&#xff0c;因为本身需要作为服务端进行主动消息推送&#xff0c;因此使用Gateway方式&#xff0c;如果不需要的可以不采用这种形式&#xff0c;以下内容仅为参考&#xff0c;具体业务场景&am…

【USB】CC检测

CC信号有两根线&#xff0c;CC1和CC2&#xff0c;大部分USB线&#xff08;不带芯片的线缆&#xff09;里面只有一根CC线&#xff0c;DFP可根据两根CC线上的电压&#xff0c;判断是否已经插入设备。通过判断哪根CC线上有下拉电阻来判断方向&#xff0c;下图的说明已经非常清晰。…