多线程概念,常用接口与多进程之间的比较

news2024/11/29 6:39:41

多线程概念,常用接口与多进程之间的比较

  • 多线程概念与常用接口
    • 多线程概念与相对于线程的区别
      • 什么是多线程(概念)
      • 进程和线程的区别
      • 在Linux系统下,进程和线程的区别如下:
      • 多进程和多线程优缺点比较:
      • 在多任务处理中,启动多少执行流比较合适
    • 线程控制
      • 线程的创建
      • 线程的终止
      • 线程的等待
      • 线程的分离

在这里插入图片描述

多线程概念与常用接口

多线程概念与相对于线程的区别

什么是多线程(概念)

多线程是指在同一个进程内同时执行多个线程,每个线程都是独立的执行流,都有自己的程序计数器、堆栈、寄存器和状态等信息,但共享同一个进程的地址空间和资源(在进程的共享区)。多线程可以提高程序的并发性和响应速度,使得程序能够更加有效地利用计算机的多核心和多任务处理能力。
补充:什么是高并发:
高并发性是指系统能够同时处理大量的并发请求,保持系统的高效性和可用性。 在计算机领域,高并发性通常指在单位时间内处理的请求量非常大,例如每秒处理几百万甚至几千万个请求。高并发性通常是对于网络服务、Web应用程序、数据库系统、操作系统等需要处理大量请求的系统而言的。

进程和线程的区别

进程是计算机中正在执行的程序实例,是资源分配的单位。当程序被启动时,操作系统为该程序分配一段内存空间,这段内存空间包含了程序的代码、数据和堆栈等信息,并为该程序创建一个进程实例。进程包括了程序的所有状态,例如程序计数器、寄存器、堆栈指针和打开的文件等。操作系统利用进程的信息来控制程序的执行和资源的分配,使多个程序可以同时在计算机上运行。

进程是计算机中最基本的执行单元,每个进程都拥有自己的内存空间、资源和执行上下文,这使得多个进程可以同时在计算机上运行,而不会相互干扰。进程之间可以通过进程间通信来进行数据交换和协作,

线程是进程中的执行单元,每个进程可以包含多个线程。 线程共享进程的内存空间和资源,每个线程拥有自己的执行上下文和栈。线程可以协作完成一个任务,也可以同时执行多个任务,这使得多线程程序可以充分利用计算机的多核处理能力,提高程序的性能和响应速度。

因此,进程和线程都是计算机中的执行单元,进程是操作系统中资源分配的最小单位,线程是进程中的执行单元,一个进程可以包含多个线程。两者的区别在于进程是操作系统中的基本单位,而线程是进程中的执行单元,进程之间相互独立,线程之间共享进程的资源和上下文。

在Linux系统下,进程和线程的区别如下:

  1. 资源占用:每个进程都会占用一定的系统资源,包括独立的地址空间、文件描述符、内存和系统调用等,而线程则共享进程的资源,包括地址空间、文件描述符、内存和系统调用等。

  2. 上下文切换:进程之间的切换需要进行完整的上下文切换,包括寄存器、内存和I/O等,而线程之间的切换只需要切换线程的上下文和栈,比进程切换更快。

  3. 调度:进程之间的调度由操作系统负责,每个进程有自己的调度策略和优先级,而线程之间的调度由进程内的线程库负责,根据线程的优先级和调度算法来调度线程。

  4. 通信:进程之间的通信需要使用进程间通信机制(IPC),例如管道、信号、共享内存和消息队列等,而线程之间的通信可以直接通过共享内存和进程内部的信号量、互斥锁和条件变量等进行通信。

  5. 安全性:进程之间的数据隔离更加彻底,可以提供更高的安全性,而线程之间共享同一个地址空间,可能会出现数据互相干扰的问题,需要进行同步和互斥等操作来保证数据的一致性和安全性。

因此,在Linux系统下,进程和线程都是执行单元,进程是资源分配的基本单位,线程是进程中的执行单元。 进程之间的隔离更加彻底,线程之间的切换更加快速,但需要考虑线程间的同步和互斥问题。

在Linux下,进程和线程都是通过pcb实现的,进程和线程都是以任务(task)的形式存在的,每个任务都对应着一个PCB,任务的切换实际上就是PCB的切换。由于线程共享同一个进程的地址空间和资源,因此Linux中线程的PCB与进程的PCB在结构上是有所区别的,线程的PCB通常称为线程描述符(Thread Descriptor)或轻量级进程(LWP,Light-weight Process)。Linux中线程的PCB与进程的PCB在结构上的区别在于,线程的PCB只记录了线程的部分状态,如堆栈指针、寄存器值、调度策略等,而没有单独的地址空间和文件描述符等资源,这些资源是共享给整个进程的。因此,线程切换的代价相对进程切换要小得多,这也是多线程应用程序的一个重要优势。

多进程和多线程优缺点比较:

多进程和多线程都是用来实现并发的技术,但它们各自具有一些优缺点,下面对它们进行比较:

多进程的优点:

稳定性(健壮性强):每个进程都是独立的,一个进程崩溃不会影响其他进程,因此系统的稳定性更高。

安全性:进程间的内存空间是独立的,不同进程之间的数据不会互相干扰,因此系统的安全性更高。

可扩展性:可以利用多台计算机的资源,将多个进程分配到不同的计算机上运行,从而实现更高的可扩展性。
所以在例如网络服务器,shell程序这种对于主程序要求高的情况,多使用多进程,其他情况尝尝使用多线程提高效率。

多进程的缺点:

资源消耗:每个进程都需要独立的地址空间、文件描述符、信号处理等资源,因此创建和维护多个进程的开销相对较大。

进程间通信:进程间通信需要额外的开销,而且往往需要使用复杂的通信机制来实现。

上下文切换:进程切换的开销相对较大,需要保存和恢复更多的状态信息,因此系统的性能可能受到影响。

多线程的优点:

资源节约:多个线程可以共享同一个进程的地址空间、文件描述符、信号处理等资源,因此创建和维护多个线程的开销相对较小

响应速度:线程切换的开销相对较小,可以更快地响应用户的请求,提高系统的响应速度。

简单易用:线程的创建,销毁和管理相对较简单,程序员可以更方便地利用多线程实现并发操作。

多线程的缺点:

稳定性:多个线程共享同一个地址空间,一个线程的错误可能会影响其他线程或整个进程,因此系统的稳定性可能会受到影响。

安全性:多个线程共享同一个内存空间,数据竞争和死锁等问题可能会出现,程序员需要使用同步机制来保证线程的安全性。

调试困难:多个线程同时运行,程序的执行流程可能会变得复杂和难以调试,特别是在多线程并发环境下,程序员需要花费更多的时间和精力来调试程序。

在多任务处理中,启动多少执行流比较合适

在多任务处理中,使用多执行流(多线程)可以更加充分的利用计算机资源,提高执行效率
但是执行流越多,cpu切换调度就越频繁,如果执行流太多,返回会造成切换调度消耗了大部分的资源。在任务处理中,程序一般分为俩种程序:

  1. cpu密集型程序:一段程序中几乎都是数据的运算(对cpu的使用率非常高)
  2. IO密集型程序:一段程序中大部分是IO操作(大部分时间都是在进行IO操作以及等待,因此对CPU使用率并不高)
    因此,不同的程序对于CPU的要求是不一样的,因此多执行流没有什么固定的数量,最好是经过压力测试找到最合适的数量

线程控制

主要介绍的是线程的接口,其实都是大佬们封装的库函数,因为linux操作系统并没有直接向上层提供线程系统调用接口。
在linux下创建一个线程,其实就是创建一个pcb,多个pcb指向同一个虚拟空间,因此上层接口就要实现找到pcb并对其进行一些控制

线程的创建

#include <pthread.h>

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

参数thread是一个指向pthread_t类型变量的指针,用于存储新线程的ID(线程的操作句柄);
参数attr是一个指向pthread_attr_t类型变量的指针,用于指定新线程的属性,如果为NULL,则使用默认属性;
参数start_routine是一个指向函数的指针(函数指针),该函数是新线程的启动函数,即线程的入口函数,这个线程调度运行的就是这个函数,我们知道线程就是控制进程中的一部分,所以线程就是控制调度这一部分函数;
参数arg是传递给启动函数(rounite)的参数。
功能即:创建一个线程,指定这个线程运行的函数routine,并且给这个函数传入一个数据arg,成功返回0,不成功返回非0值。

操作演示:

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>

void *thread_entry(void* arg){
    while (1)
    {
        printf("i am ordinary thread:%s\n",arg);
    }
    
}

int main()
{
    pthread_t tid;
    void * arg = "hello";
    int ret =  pthread_create(&tid,NULL,thread_entry,arg);
    if(ret!=0){
        printf("pthread error\n");
        return -1;
    }
    while(1){
        printf("i am main thread\n");
    }
    return 0;
}

普通线程一旦创建出来,创建出来的这个线程调度的是传入线程的入口函数,因此主进程也可以走下来,不等普通线程结束,并发进行。创建一个线程,其实就是让操作系统提供一个执行流。至于让线程做什么,取决于它的入口函数。

一个执行流在cpu运行的时间是一个时间片,谁先运行不一定,取决于操作系统的调度。主进程和子线程是并发执行的,它们的执行顺序是由系统调度器决定的。在多线程程序中,所有线程都可以并行运行,因此不能保证主进程先于所有子线程执行完毕。如果需要等待所有子线程执行完毕后再退出主进程,可以使用pthread_join()等待线程退出。

线程的终止

线程其实调度运行的是线程入口函数传入的入口函数,因此其实程序线程入口函数退出了,线程也就退出了。

  1. 在线程入口函数中return
    注意:main中return退出的不仅仅是主线程,而是整个线程(退出了进程)
  2. 调用pthread_exit(void *retval):接口退出,retval是线程退出的返回值
    上边两个是主动退出,在任意地方主动调用,主动调用,谁调用谁退出
  3. 调用void pthread_cancel(pthread_t thread)接口,该函数用于向指定线程发送取消请求,如果一个线程被取消,则不能调用其返回值进行操作(比如获取退出码进行线程等待,没有实际的处理意义)。假设我现在在主线程运行三秒后让普通线程退出,我就可以使用一个计时器,在主线程运行三秒后,调用接口让普通线程取消。

线程的等待

主线程退出,其实并不会影响其他线程的运行,一个线程被释放之后,它所占用的资源并不会立即释放。相反,线程的资源会在该线程结束后被释放。

当一个线程调用pthread_exit()函数或者线程函数执行完毕退出时,该线程会被标记为“已终止”,但是它所占用的资源并不会立即释放。线程的资源包括堆栈、寄存器、线程控制块等等。这些资源会被操作系统回收,但是具体的时间是不确定的,取决于操作系统的实现。通常,操作系统会在一段时间后回收线程资源,以确保资源能够被充分利用。

需要注意的是,如果一个线程没有被正确地销毁,即没有调用pthread_join()或pthread_detach()函数来回收线程资源,那么线程的资源将一直存在,直到进程结束。这会导致内存泄漏等问题,因此在编写多线程程序时需要特别注意线程的正确销毁和资源回收。

如果主线程调用pthread_exit()或者return语句退出,那么它所创建的其他线程将会继续运行,直到它们自己结束或被取消。但是如果主线程通过调用exit()函数、abort()函数或者收到信号等方式退出,那么整个进程将会被终止,所有线程都将被强制结束,即使它们没有执行完毕。这种情况下,其他线程的状态和数据都将丢失,可能会导致数据不一致或资源泄漏等问题。

等待:等待指定线程退出,获取退出的线程返回值,回收退出线程的所有资源

pthread_join(pthread_t thread, void **retval):该函数用于等待指定线程的结束,并获取线程的返回值。调用线程会一直阻塞,直到指定线程退出为止。如果不需要获取线程的返回值,返回的是一个地址,所以传入二级指针。可以将retval参数设置为NULL。thread是获取线程的tid。被等待的线程在退出时必须通过pthread_exit()函数返回一个值,否则无法获取到有效的返回值。成功返回0,失败返回一个错误编号。

pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex):该函数用于等待条件变量的触发,并在等待期间释放互斥锁。在调用该函数之前,必须先获得互斥锁,否则会导致未定义的行为。当条件变量被触发时,该函数会重新获得互斥锁,并继续执行。

注意:线程的传递需要额外的注意数据的常量。(static,malloc创建的等),如果线程被取消了,将会返回一个宏。

线程的分离

线程的分离(Detached Thread)是指将一个线程设置为分离状态,使得该线程结束后,它所占用的资源会自动被系统回收,而无需其他线程调用pthread_join()函数来获取其退出状态。在线程属性中,有一个属性叫做分离属性,默认是joinable状态,表示线程退出后,不会被自动释放资源,需要被其他线程等待,但是我们有时候并不关心一个线程的返回值,也不想等待他的退出,则这个时候会将这个分离属性设置为detach状态,表示线程退出后,会自动释放所有资源,不需要被等待(等待会出错)
int pthread_detach(pthread_t tid);//设置指定线程的分离属性为detach
在这里插入图片描述

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

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

相关文章

国产仪器 1612A无线信道仿真器

1612A无线信道仿真器是一款专门的无线信道仿真设备&#xff0c;可准确实时仿真复杂的无线信道特征&#xff0c;包含路径损耗、延迟、多径衰落以及噪声等&#xff0c;重现真实的信号传播环境&#xff0c;用于对比测试及反复测试&#xff0c;加快问题的发现及解决的过程。本产品突…

canvas学习笔记

其实还有react还没有学&#xff0c;但是公司技术栈里面有canvas&#xff0c;所以先系统学习一下canvas 一、canvas 简介 ​<canvas> 是 HTML5 新增的&#xff0c;一个可以使用脚本(通常为 JavaScript) 在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也…

微服务之服务间通信:关于Feign的练习demo

一、主要流程&#xff1a; 创建两个最基础的springboot项目调用方引入Feign的依赖在调用方服务项目中创建agent接口类&#xff0c;类使用FeignClient注解&#xff0c;注解重点配置url&#xff08;即被调用方服务所在的地址ip端口号&#xff09;、写接口方法等。在具体业务代码…

Softing邀您参加第16届诊断大会

第16届机电车辆系统诊断大会将于2023年5月23-24日在德累斯顿举行。今年的主题将再次围绕预测性维护、远程诊断、机器学习、标准化以及检验和ePTI方面的当前挑战和新技术展开。 在5月23日&#xff08;周二&#xff09;&#xff0c;Softing汽车电子的创新与测试经理-Jrgen Heilm…

【FMC147】 基于VITA57.4标准的单通道6GSPS 12位采样ADC,单通道 6GSPS 16位采样DAC子卡模块

板卡概述 FMC147是一款单通道6.4GSPS&#xff08;或者配置成2通道3.2GSPS&#xff09;采样率的12位AD采集、单通道6GSPS&#xff08;或配置成2通道3GSPS&#xff09;采样率16位DA输出子卡模块&#xff0c;该板卡为FMC标准&#xff0c;符合VITA57.4规范&#xff0c;该模块可以作…

React 内 JSX了解及使用

Jsx的全称是Javascript XML&#xff0c;react定义的一种类似XML的JS拓展语法&#xff1a;JSXML&#xff0c;使我们可以用类似于xml方式描述视图。 本质是React.createElement(component, props, ...children) 的语法糖 原理&#xff1a;babel-loader会预编译JSx为React.creat…

【论文阅读-TPAMI2021】Curriculum Learning(课程学习)综述

简介 Curriculum learning (CL&#xff0c;课程学习)是一种模型训练策略&#xff0c;通过先让模型学习简单数据后再学习困难数据的方式模拟学生进行课程学习的场景。通用的课程学习框架为Difficulty Measurer &#xff08;困难程度评估&#xff09; Training Scheduler&#x…

基于分页实现数据的增删查改

一&#xff1a;主要思路 1:分页功能: 新建一个实体类&#xff0c;用来存储每页数据&#xff0c;数据量&#xff0c;页码&#xff0c;页数&#xff0c;下一页&#xff0c;上一页的相关信息。 Data public class PageModel<T> {//本页数据private List<T> pageDat…

当年差点把我折磨疯的DOS下的打字游戏

最近群里有人喊有没有好用的打字游戏&#xff0c;想给学生找点事儿做&#xff0c;省得他们调皮。我就突然想到当年差点把我折磨疯的这个TT游戏。 记得之前有一个版本我可以使用&#xff0c;打开一看自己当初还写了一段好简单的介绍&#xff0c;就一行字 把tt.com文件拖到DOSB…

C. Plasticine zebra(思维)

Problem - C - Codeforces 这道题目要求你从一个由b和w组成的字符串中选择连续的、交替出现的颜色块来拼出斑马纹路&#xff0c;然后对这个字符串进行零次或多次的切分、翻转和粘合操作&#xff0c;以达到最大的斑马长度。 具体来说&#xff0c;Grisha想要选择若干连续的、交替…

STM32HAL库 总线舵机驱动库的编写

STM32 HAL库 总线舵机驱动库的编写 文章目录 STM32 HAL库 总线舵机驱动库的编写1 理论基础1.1 硬件1.2 电路图1.3 原理1.4 通信协议 2 程序编写2.1 cube mx设置&#xff08;1&#xff09;USART1设置&#xff08;2&#xff09;USART3设置 2.2 程序编写&#xff08;1&#xff09;…

【C程序设计】——最简单的C语言程序

目录 &#x1f34a;&#x1f34a;一、最简单的C语言程序 1.1 最简单的C语言程序举例 1.2 C语言程序的结构 首先&#xff0c;让我们先了解一下C语言的特点&#xff1a; 语言简洁、紧凑&#xff0c;使用方便、灵活&#xff1b;运算符丰富&#xff1b;数据类型丰富&#xff1b…

806. 写字符串需要的行数

806. 写字符串需要的行数 一、题目描述&#xff1a; 我们要把给定的字符串 S 从左到右写到每一行上&#xff0c;每一行的最大宽度为100个单位&#xff0c;如果我们在写某个字母的时候会使这行超过了100 个单位&#xff0c;那么我们应该把这个字母写到下一行。我们给定了一个数…

关于ubuntu20.04 apt 安装源中搜索不到最新版本gcc 12的问题

一、问题描述 最近在搞Open 3d 点云point cloud 相关的东西&#xff0c;过程需要安装较高版本的cmake 3.20版本以上&#xff0c;3.20版本又需要gcc 更高版本 至少11.0以上&#xff0c;理论上本机配置的有 ubuntu 官方的源和阿里云的源&#xff0c;不过 通过搜索就只能搜索安装的…

大模型中的temperature参数+随机采样策略

一、问题来源&#xff1a; 使用GPT-3.5的时候发现相同的输入会得不一样的结果 二、根因定位&#xff1a; 核心就在于采样策略&#xff0c;一图胜千言&#xff1a; 上图中语言模型 (language model) 的预测输出其实是字典中所有词的概率分布&#xff0c;而通常会选择生成其中…

【JavaScript全解析】ES6定义变量与箭头函数详解

箭头函数可以说是ES6的一大亮点,使用箭头函数,可以简化编码过程,使代码更加的简洁 本文由千锋前端老师独家创作&#xff0c;主要给大家介绍了关于ES6中箭头函数的相关资料,文中通过实例代码介绍的非常详细,觉得有帮助的话可以【关注】持续追更~ ES6定义变量 我们现在知道定义…

二、easyUI中的layout(布局)组件

1.layout&#xff08;布局&#xff09;组件的概述 布局容器有5个区域&#xff1a;北、南、东、西和中间。中间区域面板是必须的&#xff0c;边缘的面板都是可选的。每个边缘区域面板都可以通过拖拽其边框改变大小&#xff0c;也可以点击折叠按钮将面板折叠起来。布局可以进行嵌…

前端单元测试是怎么做的?

为什么要做单元测试 1. 执行单元测试&#xff0c;就是为了证明这段代码的行为和我们期望的一致 2. 进行充分的单元测试&#xff0c;是提高软件质量&#xff0c;降低开发成本的必由之路 3. 在开发人员做出修改后进行可重复的单元测试可以避免产生那些令人不快的负作用 怎么去…

YOLOv8中的C2f的详细解读

C2f的结构图,看不懂没关系,继续往下看,一定会看懂的!!!首先是C2f的逻辑代码: class C2f(nn.Module):# CSP Bottleneck with 2 convolutionsdef __init__(self, c1, c2, n=1, shortcut=

排序算法的比较与java实现

冒泡排序 基本思想: 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。 对每一对相邻元素作同样的工作&#xff0c;从开始第一对到结尾的最后一对。在这一点&#xff0c;最后的元素应该会是最大的数。 针对所有的元素重复以上的步骤&#xff0c;除了最后一个。…