当前位置: 首页 > news >正文

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

相关文章:

Linux - 线程基础

文章目录 1.什么是线程2.线程vs进程3.线程调度4.线程控制4.1 POSIX线程库4.2创建线程4.3线程终止4.4线程等待4.5线程分离 5、线程封装 1.什么是线程 在Linux操作系统中&#xff0c;线程是进程内部的一个执行流。在Linux操作系统下&#xff0c;执行流统称为轻量级进程&#xff0…...

网络爬虫——分布式爬虫架构

分布式爬虫在现代大数据采集中是不可或缺的一部分。随着互联网信息量的爆炸性增长&#xff0c;单机爬虫在性能、效率和稳定性上都面临巨大的挑战。分布式爬虫通过任务分发、多节点协作以及结果整合&#xff0c;成为解决大规模数据抓取任务的核心手段。 本节将从 Scrapy 框架的…...

RT_Thread内核源码分析(三)——线程

目录 1. 线程结构 2. 线程创建 2.1 静态线程创建 2.2 动态线程创建 2.3 源码分析 2.4 线程内存结构 3. 线程状态 3.1 线程状态分类 3.2 就绪状态和运行态 3.3 阻塞/挂起状态 3.3.1 阻塞工况 3.4 关闭状态 3.4.1 线程关闭接口 3.4.2 静态线程关闭 3.4.3 动态线程关…...

正排索引和倒排索引

一、简介 正排索引&#xff1a;一个未经处理的数据库中&#xff0c;一般是以文档ID作为索引&#xff0c;以文档内容作为记录。 倒排索引&#xff1a;Inverted index&#xff0c;指的是将单词或记录作为索引&#xff0c;将文档ID作为记录&#xff0c;这样便可以方便地通过单词或…...

丹摩 | 重返丹摩(上)

目录 一.登录平台 二. 数据管理与预处理 1.数据清洗 2.数据格式转换 3.特征工程 二.数据可视化 1.快速可视化 2.数据洞察 3.自定义视图 三.技术支持与帮助 1.技术支持 (1). 帮助文档 (2). 用户社区 2.客服支持 (1). 在线客服 (2). 反馈与建议 总结 一.登录平台…...

Frontend - 防止多次请求,避免重复请求

目录 一、避免重复执行的多种情况 &#xff08;一&#xff09;根据用途 &#xff08;二&#xff09;根据用户操作 二、具体实现 &#xff08;一&#xff09;“Ajax ”结合disabled (防止多次请求)&#xff0c;避免多次点击重复请求 1. 适用场景 2. 解决办法 3. 示例 &…...

RHCE的学习(22)

第四章 流程控制之条件判断 条件判断语句是一种最简单的流程控制语句。该语句使得程序根据不同的条件来执行不同的程序分支。本节将介绍Shell程序设计中的简单的条件判断语句。 if语句语法 单分支结构 # 语法1&#xff1a; if <条件表达式> then指令 fi #语法2&#x…...

【前端知识】简单讲讲什么是微前端

微前端介绍 一、定义二、背景三、核心思想四、基本要素五、核心价值六、实现方式七、应用场景八、挑战与解决方案 什么是single-spa一、核心特点二、核心原理三、应用加载流程四、最佳实践五、优缺点六、应用场景 什么是 qiankun一、概述二、特点与优势三、核心功能四、使用场景…...

AWS IAM

一、介绍 1、简介 AWS Identity and Access Management (IAM) 是 Amazon Web Services 提供的一项服务,用于管理 AWS 资源的访问权限。通过 IAM,可以安全地控制用户、组和角色对 AWS 服务和资源的访问权限。IAM 是 AWS 安全模型的核心组成部分,确保只有经过授权的用户和应…...

丹摩|丹摩助力selenium实现大麦网抢票

丹摩&#xff5c;丹摩助力selenium实现大麦网抢票 声明&#xff1a;非广告&#xff0c;为用户体验 1.引言 在人工智能飞速发展的今天&#xff0c;丹摩智算平台&#xff08;DAMODEL&#xff09;以其卓越的AI算力服务脱颖而出&#xff0c;为开发者提供了一个简化AI开发流程的强…...

基于Qt/C++/Opencv实现的一个视频中二维码解析软件

本文详细讲解了如何利用 Qt 和 OpenCV 实现一个可从视频和图片中检测二维码的软件。代码实现了视频解码、多线程处理和界面更新等功能&#xff0c;是一个典型的跨线程图像处理项目。以下分模块对代码进行解析。 一、项目的整体结构 项目分为以下几部分&#xff1a; 主窗口 (M…...

智慧理财项目测试文档

目录 幕布思维导图链接&#xff1a;https://www.mubu.com/doc/6xk3c7DzgFs学习链接&#xff1a;https://www.bilibili.com/video/BV15J4m147vZ/?spm_id_from333.999.0.0&vd_source078d5d025b9cb472d70d8fda1a7dc5a6智慧理财项目测试文档项目介绍项目基本信息项目业务特性系…...

R | 统一栅格数据的坐标系、分辨率和行列号

各位同学&#xff0c;在做相关性等分析时&#xff0c;经常会遇到各栅格数据间的行列号不统一等问题&#xff0c;下面的代码能直接解决这类麻烦。以某个栅格数据的坐标系、分辨率和行列号为准&#xff0c;统一文件夹内所有栅格并输出到新的文件夹。 代码只需要更改输入输出和ti…...

C++学习——编译的过程

编译的过程——预处理 引言预处理包含头文件宏定义指令条件编译 编译、链接 引言 C程序编译的过程&#xff1a;预处理 -> 编译&#xff08;优化、汇编&#xff09;-> 链接 编译和链接的内容可以查阅这篇文章&#xff08;点击查看&#xff09; 预处理 编译预处理是指&a…...

当你要改文件 但是原来的文件内容又不能丢失的时候,拷贝一份(备注原来的),然后添加后缀:.bak

当你要改文件 但是原来的文件内容又不能丢失的时候&#xff0c;拷贝一份&#xff08;备注原来的&#xff09;&#xff0c;然后添加后缀&#xff1a;.bak &#xff01;&#xff01;&#xff01;文件不要直接删除&#xff0c;若你以后要还原的话会找不到...

MATLAB神经网络(五)——R-CNN视觉检测

5.1 目标分类、检测与分割 在计算机视觉领域&#xff0c;目标分类、检测与分割是常用计数。三者的联系与区分又在哪呢&#xff1f;目标分类是解决图像中的物体是什么的问题&#xff1b;目标检测是解决图像中的物体是什么&#xff0c;在哪里的问题&#xff1b;目标分割时将目标和…...

mock.js:定义、应用场景、安装、配置、使用

前言&#xff1a;什么是mock.js&#xff1f; 作为一个前端程序员&#xff0c;没有mockjs你不感觉很被动吗&#xff1f;你不感觉你的命脉被后端那个男人掌握了吗&#xff1f;所以&#xff0c;我命由我不由天&#xff01;学学mock.js吧&#xff01; mock.js 是一个用于生成随机…...

【GAT】 代码详解 (1) 运行方法【pytorch】可运行版本

GRAPH ATTENTION NETWORKS 代码详解 前言0.引言1. 环境配置2. 代码的运行2.1 报错处理2.2 运行结果展示 3.总结 前言 在前文中&#xff0c;我们已经深入探讨了图卷积神经网络和图注意力网络的理论基础。还没看的同学点这里补习下。接下来&#xff0c;将开启一个新的阶段&#…...

Transformer中的Self-Attention机制如何自然地适应于目标检测任务

Transformer中的Self-Attention机制如何自然地适应于目标检测任务&#xff1a; 特征图的降维与重塑 首先&#xff0c;Backbone&#xff08;如ResNet、VGG等&#xff09;会输出一个特征图&#xff0c;这个特征图通常具有较高的通道数、高度和宽度&#xff08;例如CHW&#xff…...

2411rust,1.75.0

原文 Rust团队很高兴地声明推出Rust的新版本1.75.0. 如果你rustup安装了以前版本的Rust,你可如下取1.75.0: $ rustup update stable1.75.0稳定版中的功能 async fn和特征中的返回位置impl Trait. 指针字节偏移API 原始指针(*const T和*mutT)过去主要支持,T为单位的操作.如…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...