线程控制--Linux

news2024/10/6 22:21:28

文章目录

  • 线程理解
  • 线程的优点与缺点
  • 进程的多个线程共享
  • 线程控制
    • 线程创建
    • 线程终止
    • 线程等待
    • 线程分离
  • 总结

线程理解

谈及线程,就不得不谈起进程与线程的关系了。学习完前面有关进程的知识,之前我们对进程的定义是:内核数据结构+代码和数据。但是今天学习完线程的知识后,再这样定义就是不对的了,因为线程也可以这样定义,这样说也反映了线程和进程极其相似,都有内核数据结构+代码和数据。至于线程到底是个什么东西,接下来我们通过图解来展示。

image-20230125112927252

在CPU执行一个进程时,不再是直接看到整个进程的PCB了,而是看到每个线程的PCB,同时推进多个线程的运行。因此,在一个程序中一个执行线路被称为线程(thread),线程就是执行流,是基本的调度单位。那么此时的进程就称为:承担分配系统资源的基本实体。

线程的优点与缺点

优点 :

1.创建一个新线程的代价要比创建一个新进程小得多

2.与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多

3.线程占用的资源要比进程少很多

4.能充分利用多处理器的可并行数量

5.在等待慢速I/O操作结束的同时,程序可执行其他的计算任务

6.计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现

7.I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

缺点:

1.性能损失

​ 一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集

​ 型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额

​ 外的同步和调度开销,而可用的资源不变。

2.健壮性降低

​ 编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享

​ 了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

3.缺乏访问控制

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

4.编程难度提高

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

进程的多个线程共享

同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义

一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

文件描述符表

每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)

当前工作目录

用户id和组id

线程共享进程数据,但也拥有自己的一部分数据:

线程ID

一组寄存器

errno

信号屏蔽字

调度优先级

进程和线程的关系如下图:

image-20230125115907266

线程控制

由于Linux没有原生的线程概念以及对应的用户级函数接口,因此衍生出了一个关于线程的库:pthread库,其中绝大部分的函数都是由pthread_开头,在程序中要包含头文件<pthread.h>,并在编译时要加上链接选项 -lpthread。以下所有有关线程的函数调用都要满足以上条件。

image-20230125222147489

生成可执行文件mythread

线程创建

image-20230125123817146

参数

thread:返回线程ID

attr:设置线程的属性,attr为NULL表示使用默认属性

start_routine:是个函数地址,线程启动后要执行的函数

arg:传给线程启动函数的参数

返回值:成功返回0;失败返回错误码

#include<iostream>
#include<cstdio>
#include<pthread.h>
#include<unistd.h>
using namespace std;
void* startRoutine(void* arg)//启动函数
{
    const char* name=static_cast<const char*>(arg);
    while(true)
    {
        printf("%s 正在运行……\n", name);
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    int n= pthread_create(&tid,nullptr,startRoutine,(void*)"thread1");

    while(true)
    {
        cout <<"main线程正在运行……"<<endl;
        sleep(1);
    }
    
    return 0;
}

image-20230125124841443

关于上面参数中的thread参数,这是一个输出型参数,调用后该参数就是该线程在虚拟内存中的地址空间,pthread_t类型的线程ID,本

质就是一个进程地址空间上的一个地址。这里其实还有另一个函数可以调用,同样可以显示线程的地址空间:pthread_self

image-20230125213055402

参数:无

返回值:线程ID

功能:得到调用该函数线程的ID

根据这个函数,上面的代码更改一下也是可以完成相同的工作,并附加对应的线程ID。

#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void printTid(const char *name, const pthread_t &tid)
{
    printf("%s 正在运行, thread id: 0x%x\n", name, tid);
}
void *startRoutine(void *arg)
{
    const char *name = static_cast<const char *>(arg);
    while (true)
    {
        printTid(name, pthread_self());
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, startRoutine, (void *)"thread1");

    while (true)
    {
        printTid("main thread",pthread_self());
        sleep(1);
    }

    return 0;
}

image-20230125213615465

线程终止

线程的终止一般是调用了某些函数和代码编写的错误才会导致。线程异常终止会导致整个进程的终止,这里我们先考虑比较正常的终止情况:调用pthread_exit、pthread_cancel或使用return直接退出(注意,return不适合在主线程中使用,会导致整个进程的结束)。

image-20230125215923882

功能:结束调用此函数的线程

参数:

retval:用户可以传对应的数据来表明退出时的状态

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

🔺需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程

函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了,就会导致线程自己的栈数据被销毁。

image-20230125220613145

功能:结束掉ID为thread的线程。

参数:

thread:线程ID

返回值:成功返回0;失败返回错误码

#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void printTid(const char *name, const pthread_t &tid)
{
    printf("%s 正在运行, thread id: 0x%x\n", name, tid);
}
void *startRoutine(void *arg)
{
    const char *name = static_cast<const char *>(arg);
    int cnt=5;
    while (true)
    {
        printTid(name, pthread_self());
        if(!cnt--)//五秒后退出线程
        {
            printf("线程: 0x%x退出\n",pthread_self());
            pthread_exit(nullptr);
        }
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, startRoutine, (void *)"thread1");

    while (true)
    {
        printTid("main thread",pthread_self());

        sleep(1);
    }

    return 0;
}

image-20230125221850453

线程等待

和进程等待类似,线程也要等待被回收资源,以免造成内存泄漏的问题。

image-20230125222633179

功能:调用该函数的线程将挂起等待,直到id为thread的线程终止

参数:

thread:线程ID

value_ptr:它指向一个指针,后者指向线程的返回值

  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参数。

返回值:成功返回0;失败返回错误码

#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void printTid(const char *name, const pthread_t &tid)
{
    printf("%s 正在运行, thread id: 0x%x\n", name, tid);
}
void *startRoutine(void *arg)
{
    const char *name = static_cast<const char *>(arg);
    int cnt = 5;

    while (true)
    {
        printTid(name, pthread_self());
        if (!cnt--)
        {
            printf("线程: 0x%x退出\n", pthread_self());
            pthread_exit((void *)666);
        }
        sleep(1);
    }
    return nullptr;
}
int main()
{
    void *retval = nullptr;
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, startRoutine, (void *)"thread1");
    pthread_join(tid, &retval); // 回收线程资源,也会导致主线程在此处挂起
    printf("有线程退出, retval: %d\n",retval);
    while (true)
    {
        printTid("main thread", pthread_self());

        sleep(1);
    }

    return 0;
}

image-20230125225656290

线程分离

默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。如果不关心线程的返回值,join是一种负担,这个时候,我们可以使用pthread_detach函数,当线程退出时,自动释放线程资源。但是值得注意的是,线程分离之后是不能够再join的,这两者冲突。

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离。

#include <iostream>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void printTid(const char *name, const pthread_t &tid)
{
    printf("%s 正在运行, thread id: 0x%x\n", name, tid);
}
void *startRoutine(void *arg)
{
    pthread_detach(pthread_self());//分离线程
    const char *name = static_cast<const char *>(arg);
    int cnt = 5;

    while (true)
    {
        printTid(name, pthread_self());
        if (!cnt--)
        {
            printf("线程: 0x%x退出\n", pthread_self());
            pthread_exit((void *)666);
        }
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, startRoutine, (void *)"thread1");
    sleep(1);
    if(pthread_join(tid,nullptr)==0)
    {
        printf("pthread wait success\n");
    }
    else
    {
        printf("pthread wait failed\n");
    }
    while (true)
    {
        printTid("main thread", pthread_self());

        sleep(1);
    }
    return 0;
}

image-20230126095318790

总结

线程控制重点掌握pthread_create、pthread_exit、pthread_cancel、pthread_join、pthread_detach这几个函数就可以,对于主线程与新线程哪个先被调度,这个我们是不清楚的,但是通过pthread_join,一定可以控制新线程先退,主线程后退,这也算是线程控制的细节了。

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

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

相关文章

C语言return的用法详解,C语言函数返回值详解

函数的返回值是指函数被调用之后&#xff0c;执行函数体中的代码所得到的结果&#xff0c;这个结果通过 return 语句返回。return 语句的一般形式为&#xff1a;return 表达式;或者&#xff1a;return (表达式);有没有( )都是正确的&#xff0c;为了简明&#xff0c;一般也不写…

python使用类装饰器生成函数的使用日志

1 什么是类装饰器 在了解类装饰器之前&#xff0c;建议大家先了解装饰器的概念。 装饰器知识快速入门链接 类装饰器是 Python 中的一种特殊类型的装饰器&#xff0c;它是一个类而不是一个函数。与函数装饰器不同&#xff0c;类装饰器可以在运行时接收参数并返回一个可调用的对…

Linux文件系统(IO缓冲区+磁盘+软硬链接)

目录 一、缓冲区 1.1 缓冲区是内存的一块存储空间 1.2 缓冲区的作用 1.3 C式缓冲区 1.3.1 C语言的FILE结构体 1.3.2 C式缓冲区刷新策略 二、OS与内核缓冲区 2.1 数据从缓冲区到磁盘 2.2 fsync() 数据免缓冲直接到磁盘文件 2.3 检验用户与内核缓冲区 三、文件系统 3…

11. 好客租房-ElasticSearch入门学习

Elaticsearch&#xff0c;简称为es&#xff0c; es是一个开源的高扩展的分布式全文检索引擎&#xff0c;它可以近乎实时的存储、检索数据&#xff1b;本身扩展性很好&#xff0c;可以扩展到上百台服务器&#xff0c;处理PB级别的数据。es也使用Java开发并使用Lucene作为其核心来…

分享144个ASP源码,总有一款适合您

ASP源码 分享144个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 144个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/15O9p6a8XlNN0u-wFKEkJqQ?pwd8354 提取码&#x…

Go 语言

Go语言是云计算时代的语言 Go语言2007年诞生于Google&#xff0c;2009年开源&#xff0c;Go语言与区块链技术一样年轻 本文是对Go语言基本语法的总结 目录 Go词法单元 token Go的token 标识符 内置数据类型标识符 常量值标识符 空白标识符 关键字 程序整体结构的关键字…

VBA提高篇_05日期时间函数

文章目录日期函数1. Date()2. Time()3. Now()时间数据解析函数时间运算函数DateDiff() 数据时间差DateAdd() 时间点指定跨越拓展日期函数 VBA中默认日期系统格式: #1/26/2023 12:20:25 # #月/日/年 时:分:秒# 1. Date() 获取当前系统的时间(年/月/日) 精度: 精确到秒 范围: 公…

Tomcat-HTTP服务器介绍、安装与使用

文章目录一、概述二、下载安装三、介绍四、启动Tomcat一、概述 Tomcat&#xff0c;是一个 HTTP 服务器&#xff0c;就是在 TCP 服务器的基础上&#xff0c;加上了一些额外的功能。使用 HTTP 服务器的 API。就可以来提取HTTP请求的内容&#xff0c;也可以构造HTTP响应 二、下载…

【GPLT 二阶题目集】L2-015 互评成绩

学生互评作业的简单规则是这样定的&#xff1a;每个人的作业会被k个同学评审&#xff0c;得到k个成绩。系统需要去掉一个最高分和一个最低分&#xff0c;将剩下的分数取平均&#xff0c;就得到这个学生的最后成绩。本题就要求你编写这个互评系统的算分模块。 输入格式&#xff…

HBase原理和设计

简介 HBase —— Hadoop Database的简称&#xff0c;Google BigTable的另一种开源实现方式&#xff0c;从问世之初&#xff0c;就为了解决用大量廉价的机器高速存取海量数据、实现数据分布式存储提供可靠的方案。从功能上来讲&#xff0c;HBase不折不扣是一个数据库&#xff0…

EcoStruxure Operator Terminal Expert 操作员终端专家

EcoStruxure Operator Terminal Expert&#xff08;以前称为 Vijeo XD&#xff09;是一款具有最新 UI 设计和手势的触摸屏配置软件&#xff0c;使您能够为 Magelis HMI 和 iPC 创建和编辑应用程序屏幕。 特点&#xff1a; 变量——内存中用于存储数据的命名空间。创建您需要的所…

MS-Model【3】:Medical Transformer

文章目录前言1. Abstract & Introduction1.1. Abstract1.2. Introduction2. Medical Transformer (MedT)2.1. Model structure2.2. Attention2.2.1. Self-Attention Overview2.2.2. Axial-Attention2.2.3. Gated Axial-Attention2.3. Local-Global Training2.4. Loss funct…

定位 position属性 相对定位 绝对定位 固定定位 定位下的居中 多个定位元素重叠时 补充

目录定位position属性相对定位绝对定位固定定位定位的做法&#xff1a; 定位下的居中多个定位元素重叠时补充定位 视觉格式化模型&#xff0c;大体上将页面中盒子的排列分为三种方式&#xff1a; 常规流浮动&#xff1a;float定位&#xff1a;position 定位&#xff1a;手动…

MySQL —— 数据类型

目录 一、数据类型的分类 二、数值类型 1. tinyint类型 2. bit类型 3. float类型 4. decimal类型 三、字符串类型 1. char类型 2. varchar类型 3. char和varchar的比较 四、时间日期类型 五、enum和set类型 一、数据类型的分类 分类数据类型说明数值类型BIT(M)位…

《深入浅出计算机组成原理》学习笔记 Day11

浮点数1. 浮点数的二进制转化2. 浮点数的加法和精度损失参考1. 浮点数的二进制转化 以 9.1109.1_{10}9.110​ 为例。910100129_{10} 1001_2910​10012​&#xff0c;再把小数位转换为二进制。以 0.100120.1001_20.10012​ 为例&#xff1a; 0.1001212−102−202−312−40.562…

吊打大厂:内核级安卓系统优化软件 | 雪豹速清app官网下载

雪豹速清app是当前非常热门的一款安卓系统垃圾清理优化工具&#xff0c;具有雪豹文件管理器、大文件查找、冗余文件/重复文件清理、安卓内核级垃圾清理、QQ微信专清、文件秒搜、M3U8视频合并、微信语音导出、伪装音视频查找、安装包提取等诸多特色实用功能&#xff0c;雪豹速清…

LCR TC1 测试仪

用于检测NPN PNP 晶体管 电阻 电容二极管 三极管 NMOS PMOS IGBT JFET 可控硅 红外波形 &#xff0c;具有自校准功能。我手里的是TC-V2.12k 版本红外检测方法 &#xff1a;红外遥控器对准接收口&#xff0c;然后按下发送 即可检测 检测出 usercode 和datacode产品参数1.8寸屏幕…

Python内置包Tkinter的重要控件(上)

学习了这么久的Tkinter&#xff0c;基本上把Tkinter的重要控件都学了一遍&#xff0c;本文主要对其所有重要控件以及重要函数做一个总结&#xff0c;加深对Tkinter的理解与应用。 目录 前言 控件 1. Label 2. Button 3. Entry 4. Text 5. Menu 总结 前言 包括但不限…

MyBatis(一)MyBatis概述

一、什么是框架 ● 在文献中看到的framework被翻译为框架 ● java常用的框架&#xff1a; SSM三大框架&#xff1a;SpingSpringMVCMyBatisSpringBootSpringCloud● 框架其实就是对通用代码的封装&#xff0c;提前写好了一堆接口和类&#xff0c;我们可以在做项目的时候直接引…

Golang学习日志 ━━ gin-vue-admin前后端实现tinymce编辑器的上传功能

gin-vue-admin是一套国人用golang开发的后台管理系统&#xff0c;总体还是值得推荐的&#xff0c;其前端使用element-ui&#xff0c;后端使用gin框架。 官网&#xff1a;https://www.gin-vue-admin.com/ 本文主要描述tinymce的使用&#xff0c;很简单&#xff0c;基本流程如下&…