【Linux】Linux线程

一、Linux线程的概念

1.什么是线程

1.一个进程的一个执行线路叫做线程,线程的一个进程内部的控制序列。

2.一个进程至少有一个执行线程

3.线程在进程内部,本质是在进程地址空间内运行

4.操作系统将进程虚拟地址空间的资源分配给每个执行流,就成了线程执行流。

2.线程的优点

1.创建新线程的成本远低于创建新进程。

2.与进程切换相比,线程切换需要较少的操作系统资源。

3.线程比进程占用更少的资源。

4.它们能有效地利用多处理器并行处理的能力。

5.在等待缓慢的I/O操作时,程序还可以进行其他计算任务。

6.在多处理器系统中,计算密集型应用通过分解计算任务到多个线程中来运行。

7.I/O密集型应用通过重叠I/O操作来提升性能,允许线程同时等待多个I/O操作。

3.线程的缺点

a.增加额外开销

计算密集型线程,若很少受外部事件阻塞,通常不能与其他线程共享同一处理器。当计算密集型线程的数量超过可用处理器时,可能会导致显著的性能损失。这种性能损失主要是由于增加了额外的同步和调度开销,而可用资源保持不变。

b.线程安全

多线程编程需要更全面和深入的考量。在多线程程序中,由于时间分配上的微小偏差或共享了不应共享的变量,可能会产生不良影响,这意味着线程之间缺乏必要的保护。

c.缺乏访问控制

进程是访问控制的基本单位。在一个线程中调用某些操作系统函数可能会影响整个进程。

d.编程难度提高

编写和调试多线程程序比单线程程序更加困难。

e.线程异常

如果单个线程出现除零错误或野指针问题导致崩溃,整个进程也会因此崩溃。线程是进程的执行分支,一旦线程发生异常,就相当于进程发生异常,这将触发信号机制并终止进程。一旦进程终止,该进程内的所有线程也将随之退出。

二、Linux下进程与线程的区别

进程是资源分配的基本单位 线程是调度的基本单位

1.资源的区别

线程共享进程的数据,同时也有它们自己独立的数据部分:

线程ID

一组寄存器

errno

信号屏蔽字

调度优先级

进程中的多个线程共享同一个地址空间,因此Text Segment、Data Segment都是共享的。如果定义了一个函数,它可以在所有线程中被调用;如果定义了一个全局变量,它也可以在所有线程中被访问。除此之外,所有线程还共享其他进程资源和环境:

文件描述符表

每种信号的处理方式

当前工目录

用户id和组id

2.切换开销

从一个进程切换到另一个进程,需要保存当前进程的状态并恢复新进程的状态,这个过程称为上下文切换。而线程切换的开销较小,因为线程共享相同的地址空间,不需要改变地址空间的映射关系。

3.同步与通信

进程之间的通信需要进行进程间通信(IPC),如管道、消息队列等,这些方法效率较低。而线程之间的通信可以直接通过共享内存、全局变量等方式进行,效率较高。

4.单元性

进程是独立的执行单位,一个进程崩溃不会影响其他进程。而线程共享同一个进程的资源,一个线程崩溃可能导致整个进程崩溃。

三、Linux线程控制

1.POSIX线程库

POSIX线程库(POSIX Threads)也被称为pthread库,是一套线程创建、同步、调度和管理的标准接口,定义了在多线程程序中使用线程的API和语义。POSIX线程库通常用于UNIX和类UNIX操作系统(如Linux)上。

POSIX线程库的主要特点包括:

  1. 标准化接口: POSIX线程库定义了一套标准的线程操作接口,包括线程的创建、销毁、同步等操作,使得多线程程序能够以跨平台的方式编写。

  2. 线程管理: POSIX线程库提供了对线程的管理功能,包括线程创建、退出、同步、互斥锁、条件变量、信号量等。

  3. 线程调度: POSIX线程库定义了线程的调度接口,可以设置线程的调度策略、优先级和调度参数。

  4. 线程同步: 提供了各种线程同步的机制,确保多个线程之间的协作和同步。

在使用POSIX线程库时,需要包含 <pthread.h> 头文件,并链接 -lpthread 库。

2.创建线程

使用pthread_create()函数

函数声明:

参数解析

thread:返回线程ID

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

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

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

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

错误检查:

传统的一些函数在成功时返回0,在失败时返回-1,并且会对全局变量errno赋值以指示错误类型。

而pthreads函数在出错时不会设置全局变量errno(尽管大多数其他POSIX函数会这样做),它们会通过返回值来传递错误代码。

pthreads也提供了线程内的errno变量,以便支持其他依赖errno的代码。但对于pthreads函数的错误处理,建议通过检查返回值来判断,因为这比读取线程内的errno变量要节省开销。

示例代码:

#include <iostream>
#include <unistd.h>
#include <pthread.h> 
#include "Thread.hpp"void *Printf(void *arg)
{printf("wohu!, 我是一个线程...\n");sleep(2);   }using namespace std;
int main()
{pthread_t pid;int ret = pthread_create(&pid, nullptr, Printf, nullptr);if(ret != 0){cerr << " 线程创建错误" << endl;exit(EXIT_FAILURE);}pthread_join(pid,nullptr);return 0;
}

3、线程ID与虚拟地址空间的关系

pthread_create函数生成一个线程ID,存储在第一个参数所指的地址。这个线程ID与之前提到的不同。

先前提到的线程ID是进程调度的一部分。由于线程是轻量级的进程,是操作系统调度的基本单位,因此它需要一个唯一的数值来标识。

pthread_create函数的第一个参数指向一个虚拟内存单元,这个内存单元的地址就是新线程的线程ID,这属于NPTL线程库的范围。线程库的操作都是基于这个线程ID进行的。

NPTL线程库还提供了pthread_self函数,用于获取线程自身的ID。

对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址。

4.线程终止

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

1. 从线程函数中返回。这种方法不适用于主线程,因为从main函数中返回等同于调用exit。

2. 线程可以通过调用pthread_exit来结束自己。

3. 一个线程可以通过调用pthread_cancel来终止同一进程中的另一个线程。

5.线程等待

为什么需要线程等待?

已退出的线程,其空间未被释放,仍在进程地址空间内。

新创建的线程将不会复用之前退出线程的地址空间。

使用pthread_join来进行线程等待

调用该函数的线程会挂起等待,直到标识为thread的线程终止。thread线程的终止方式不同,通过pthread_join获得的终止状态也不同,具体如下:

1. 如果thread线程通过return返回,value_ptr所指向的单元中存放的是thread线程函数的返回值。
2. 如果thread线程被其他线程以pthread_cancel调用异常终止,value_ptr所指向的单元中存放的是常量PTHREAD_CANCELED。
3. 如果thread线程通过调用pthread_exit自行终止,value_ptr所指向的单元中存放的是传递给pthread_exit的参数。
4. 如果不关心thread线程的终止状态,可以向value_ptr参数传递NULL值。

6.线程分离

默认情况下,新创建的线程是可连接的。线程结束后,必须执行pthread_join操作以释放资源,否则可能导致系统资源泄露。如果不需要线程的返回值,join操作可能显得多余。在这种情况下,我们可以设置线程在退出时自动释放其资源。

使用pthread_detach进行线程分离

int pthread_detach(pthread_t thread);

也可以自己分离

pthread_detach(pthread_self());

四、Linux线程互斥

1.线程互斥的前置概念

临界资源:指多线程执行流中共享的资源。

临界区:是指在每个线程中访问临界资源的那部分代码。

互斥:确保任何时刻只有一个执行流能进入临界区访问临界资源,起到保护临界资源的作用。

原子性:指的是不可被任何调度机制中断的操作,这种操作只有完成或未完成两种状态。

2.互斥锁mutex

多个执行流同时访问同一个临界资源会带来一些问题,这个时候就需要用到锁。

2.1比如说下面这个简单的卖票场景:

四个线程同时卖100张票

#include <iostream>
#include <unistd.h>
#include <pthread.h> 
#include "Thread.hpp"
int t = 100;void *tick(void *arg)
{while(1){if(t < 0){break;}else{usleep(1000);printf("线程%d卖: %d\n", pthread_self(), t);t--;}}return nullptr;
}int main()
{pthread_t t1,t2,t3,t4;pthread_create(&t1, nullptr, tick, nullptr);pthread_create(&t2, nullptr, tick, nullptr);pthread_create(&t3, nullptr, tick, nullptr);pthread_create(&t4, nullptr, tick, nullptr);pthread_join(t1,nullptr);pthread_join(t2,nullptr);pthread_join(t3,nullptr);pthread_join(t4,nullptr);return 0;
}

运行结果:

为什么会出现这样的情况?

1.if 语句判断条件为真以后,代码可以并发的切换到其他线程

2.usleep 这个模拟漫长业务的过程,在这个漫长的业务过程中,可能有很多个线程会进入该代码段

3.t-- 操作本身就不是一个原子操作

t--的汇编代码

可以看到t--是由三条汇编代码组成的,所以t--不是原子的。

要解决上述问题,需确保三个条件得到满足:

1.代码必须实现互斥,当一个线程进入临界区执行时,其他线程不得进入此临界区。

2.若多个线程同时请求执行临界区代码,而且临界区内没有线程在执行,则只能让一个线程进入该临界区。

3.若线程未在临界区内执行,则不应阻止其他线程进入临界区。

本质上就是加锁。

2.2互斥锁的接口

2.2.1初始化mutex

1.静态初始化

把mutex定义到全局区

2.使用init函数

2.2.2销毁mutex

使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁

2.2.2mutex加锁和解锁

成功返回0,失败返回错误码

调用 pthread_ lock 时,可能会遇到以下情况:

当互斥量处于未锁定状态时,该函数会锁定互斥量,并返回成功。

如果在调用函数时,互斥量已被其他线程锁定,或者有其他线程也在申请互斥量但未能成功竞争到,那么pthread_mutex_lock调用将会阻塞(即执行流被挂起),直到互斥量被解锁。

改进之前的代码:

加上锁

#include <iostream>
#include <unistd.h>
#include <pthread.h> 
#include "Thread.hpp"
int t = 100;pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;void *tick(void *arg)
{while(1){ pthread_mutex_lock(&mtx);if(t < 0){pthread_mutex_unlock(&mtx);break;}else{usleep(1000);printf("线程%d卖: %d\n", pthread_self(), t);t--;pthread_mutex_unlock(&mtx);}}return nullptr;
}int main()
{pthread_t t1,t2,t3,t4;pthread_create(&t1, nullptr, tick, nullptr);pthread_create(&t2, nullptr, tick, nullptr);pthread_create(&t3, nullptr, tick, nullptr);pthread_create(&t4, nullptr, tick, nullptr);pthread_join(t1,nullptr);pthread_join(t2,nullptr);pthread_join(t3,nullptr);pthread_join(t4,nullptr);return 0;
}

运行结果:

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

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

相关文章

上位机图像处理和嵌入式模块部署(树莓派4b代码优化)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 我们把程序从pc端port到嵌入式开发板上面&#xff0c;好处是降低了部署成本&#xff0c;代价是牺牲了设备性能。所以等到程序真正在开发板子上面运…

YARN详解

YARN 简介 YARN 是Yet Another Resource Negotiator的缩写。 YARN是第二代MapReduce,即MRv2,是在第一代MapReduce基础上演变而来的,主要是为了解决原始Hadoop扩展性较差,不支持多计算框架而提出的;通俗讲就是资源管理器. YARN核心思想: 将 MR1 中资源管理和作业调度两个功能分…

Linux\_c输出

第一条Linux_c输出 初界面 : ls # 显示目录下的文件cd # 进入到某个目录 # 比如 我进入了Codels # 发现没有显示, 说明为文件下为空vim cpucdoe.c # 创建一个 .c的源码文件进入到了vim的编辑界面: i # 按i 就可以进行编辑 , 下面显示插入标识在编辑模式下, 可以通…

QX-mini51学习---(2)点亮LED

目录 1什么是ed 2led工作参数 3本节相关原理图分析 4本节相关c 5实践 1什么是ed 半导体发光二极管&#xff0c;将电能转化为光能&#xff0c;耗电低&#xff0c;寿命长&#xff0c;抗震动 长正短负&#xff0c;贴片是绿点处是负极 2led工作参数 3本节相关原理图分析 当…

(五)JVM实战——JVM性能调优与监控

JVM调优案例的场景 为什么要调优&#xff1a;防止或者解决jvm虚拟机中的OOM问题&#xff1b;减少FullGC出现的频率&#xff0c;解决系统运行卡、慢问题JVM调优案例的四个方面 OOM(堆溢出)&#xff1a;java heap spaceOOM(元空间溢出)&#xff1a;MetaspaceOOM(GC overhead lim…

cmake install命令无法覆盖同名文件

文章目录 1. 问题记录2. 原因排查3. 解决方案 1. 问题记录 我有两个同名文件test.txt&#xff0c;它们内容不同&#xff0c;但时间戳相同&#xff08;文件属性中的修改时间相同&#xff09; 我希望在cmake中利用install命令&#xff0c;将${PATH_SRC}/test.txt替换${PATH_DES…

idea修改maven项目名称及子模块名称

一、修改目录名称 shift F6修改目录&#xff0c;选择“rename module and dictionary”。![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/43efd9c6af6e43ad9656455db94b37a2.png)二、修改子项目pom的 三、修改父项目pom的 四、刷新maven项目

Redis教程——主从复制

在上篇文章我们学习了Redis教程——管道&#xff0c;这篇文章学习Redis教程——主从复制。 主从复制 为了数据更加安全可靠&#xff0c;在实际的项目中&#xff0c;肯定是有多个Redis服务&#xff0c;主机Redis以写为主&#xff0c;从机Redis以读为主&#xff0c;当主机Redis…

JavaScript异步编程——03-Ajax传输json和XML

Ajax 传输 JSON JSON 的语法 JSON(JavaScript Object Notation)&#xff1a;是 ECMAScript 的子集。作用是进行数据的交换。语法更为简洁&#xff0c;网络传输、机器解析都更为迅速。 语法规则&#xff1a; 数据在键值对中 数据由逗号分隔 花括号保存对象 方括号保存数组…

Python批量计算多张遥感影像的NDVI

本文介绍基于Python中的gdal模块&#xff0c;批量基于大量多波段遥感影像文件&#xff0c;计算其每1景图像各自的NDVI数值&#xff0c;并将多景结果依次保存为栅格文件的方法。 如下图所示&#xff0c;现在有大量.tif格式的遥感影像文件&#xff0c;其中均含有红光波段与近红外…

NFTScan 与 Scattering 达成合作伙伴,双方将共同解决混合 NFT 数据需求

在区块链领域&#xff0c;NFT 的创新与发展从未停止。近日&#xff0c;NFT 数据基础设施 NFTScan 与一站式混合 NFT 交易市场 Scattering 达成合作伙伴关系&#xff0c;双方将在 NFT 数据层面展开合作&#xff0c;共同解决混合 NFT 的独特数据需求&#xff0c;Scattering 在协议…

聊聊 ASP.NET Core 中间件(三):如何创建自己的中间件?

前言 本质上&#xff0c;中间件类也是一个普通的 .NET 类&#xff0c;它不需要继承任何父类或者实现任何接口。 但是有几个约定&#xff1a; 需要有一个构造方法构造方法至少要有一个 RequestDelegate 类型的参数&#xff0c;用来指向下一个中间件。需要定义一个名字为 Invo…