线程与进程的区别、协程

1【线程与进程的区别、协程】

image-20240121155654411

【1】 进程跟线程

        进程(Process)和 线程(Thread)是操作系统的基本概念, 但是它们比较抽象, 不容易掌握。关于多进程和多线程,教科书上对经典的一句话“进程是资源分配的最小单位, 线程是CPU调度的最小单位。”

        线程是程序中一个单一的顺序控制流程。

        进程内一个相对独立的、可调度的执行的单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。

        在单个程序中同时运行多个线程完成不同的工作,称为多线程

【2】线程与进程的区别可以归纳为:

  • 地址空间和其他资源(如打开文件):进程间互相独立, 同一进程的各线程间共享。某进程期内的线程在其他进程不可见。

  • 通信:进程间通信IPC, 线程间可以直接读写进程数据段 (如全局变量)来进行通信--------需要进程同步和互斥手段的辅助, 以保证数据的一致性。

  • 调度和切换: 线程上下文切换比进程上下文切换要快得多。

  • 在多线程OS中, 进程不是一个可执行行的实体。

2【多线程和多进程的比较】

image-20240120221937440

总结: 进程和线程可以类比为火车和车厢

  • 线程在进程下行进(单纯的车厢无法运行)

  • 一个进程可以包含多个线程(一辆火车可以有多个车厢)

  • 不同进程间数据很难共享(一辆火车上的乘客很难换到另一辆火车, 比如站点换乘)

  • 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)

  • 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更消耗资源)

  • 进程间不会相互影响,一个线程挂掉了将会导致整个进程挂掉(一列火车火车不会影响到另一列火车, 但是如果一列火车上的一节车厢着火了,将会影响到该躺火车的所有车厢)

  • 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,统一火车的车厢不能在行进的不同的轨道上)

  • 进程使用的内存地址可以上锁, 即一个线程使用某写共享内存是,其他线程必须等他结束, 才能使用这一块内存。 (比如火车上的洗手间)-“互斥锁(mutex)”

  • 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量(semaphore)”

3【(为什么有了GIL锁还要互斥锁)】

是一种CPython解释器中使用的机制, 它保证统一时刻只有一个线程执行Python字节码。 这个锁的存在是为了简化CPython 的实现,因为Cpython 在设计之初并不考虑多线程的执行。

【1】注意:

GIL(全局解释器)只存在于CPython解释器中,同时Python解释器的一个特定实现。其他编程语言和环境没有这个概念。

因此在这些环境中可以自由的使用多线程来实现并进行计算。

image-20240121155739573

【2】多线程环境中, Python虚拟机的执行方法:

  1. 设置GIL

  2. 切换到一个线程去执行

  3. 运行直至指定数量的字节码指令,或者线程主动让出控制(可以调用sleep(2))

  4. 把线程设置为睡眠状态

  5. 解锁GIL

  6. 再次重复以上所有步骤

image-20240121155804352

        在单核CPU上,数百次的间隔检查才会导致一次线程切换。在多核CPU上,存在严重的线程颠簸(thrashing)。而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。

        在单核CPU上,由于只要一个核心执行代码,每次是否GIL后唤醒的线程都能够立即获取到GIL锁,因此可以实现无缝执行。

        然而,在多核CPU上,由于存在多个核心同时执行代码,当一个核心释放GIL锁后,其他核心上的线程会竞争获取GIL锁。虽然GIL可能会马上又被原来持有的核心获取到,但其他核心上被唤醒后的线程会进入待调度状态等待切换时间。这样的竞争和切换过程可能导致线程颠簸(thrashing),从而降低性能效率。

        简单的来说就是先到先的锁机制。单核可以实现无缝执行。多核则不行。

4【Python的多进程multiprocessing】

image-20240121155907247

【1】前言

        借助这个multiprocessing,你可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

【2】multiprocessing常用组件及中功能

创建管理进程模块:

  • Process(用于创建进程)

  • Pool(用于创建管理进程池)

  • Queue(用于进程通信,资源共享)

  • Value,Array(用于进程通信,资源共享)

  • Pipe(用于管道通信)

  • Manager(用于资源共享)

同步子进程模块:

  • Condition(条件变量)

  • Event(事件)

  • Lock(互斥锁)

  • RLock(可重入的互斥锁(同一个进程可以多次获得它,同时不会造成阻塞)

  • Semaphore(信号量)

【进程】Process 用于创建进程

multiprocessing模块提供了一个Process类来代表一个进程对象。

在multiprocessing中,每一个进程都用一个Process类来表示。

构造方法: Process([group [, target [, name [, args [, kwargs]]]]])

  • group: 分组,实际上不适用, 值始终为None

  • target:表示调用对象,即子进程要执行的任务,你可以传入方法名

  • name:为子进程设定名称

  • args: 要传给target函数的位置参数,以元组方式进行传入

  • kwargs:要传给target函数的字典参数,以字典方式传入

  • daemon:一个布尔值,用于设置进程是否为守护进程,守护进程会在主进程结束时会自动退出

from multiprocessing import Process
import time
​
def work(name, delay):print(f'启动 {name} 进程')time.sleep(delay)print(f'结束 {name} 进程')
​
​
if __name__ == '__main__':
​processes = []
​for i in range(5):p = Process(target = work, args=(f'工作-{i}', i))p.name = f'工作 - {i}'p.daemon = Trueprocesses.append(p)
​p.start()p.join()
​print('工作结束!')
#         
# 启动 工作-0 进程
# 结束 工作-0 进程
# 工作结束!
# 启动 工作-1 进程
# 结束 工作-1 进程
# 工作结束!
# 启动 工作-2 进程
# 结束 工作-2 进程
# 工作结束!
# 启动 工作-3 进程
# 结束 工作-3 进程
# 工作结束!
# 启动 工作-4 进程
# 结束 工作-4 进程
# 工作结束!

实例方法:

  • star():启动进程,并调用子进程中的p.run()

  • work():执行进程的target函数,通常不需要手动调用,在start() 方法内部自动调用。

  • join([timeout]):等待进程结束,可选参数timeout为超时时间,如果设置了且超过时间到达时进程仍然未结束,则会返回。

  • is_alive():返回进程是否正在执行。

  • name:进程的名称,可以通过该属性获取或设置进程的名称。

  • pid:进程的ID, 可以通过该属性获取进程的ID。

# 实例方法
​
import multiprocessing
import time
​
def worker():print(f'worker 启动 进程 {multiprocessing.current_process().name}')time.sleep(2)print(f'worker 结束 进程 {multiprocessing.current_process().name}')
​
if __name__ == '__main__':p = multiprocessing.Process(target=worker, name='工作进程')p.start()
​print(f'这是进程的 {p.name} 是否执行 {p.is_alive()}')p.join()
​print(f'这是进程的 {p.name} 是否执行 {p.is_alive()}')
​print(f'结束进程{p.name} ')# 这是进程的 工作进程 是否执行 True
# worker 启动 进程 工作进程
# worker 结束 进程 工作进程
# 这是进程的 工作进程 是否执行 False
# 结束进程工作进程 

【3】Pool (创建进程池)

image-20240121160603125

        进程池是一种常见的并发编程工具,可以看作一个公共类或者公共资源,用于管理和复用进程。它提供了一种方便来创建、调度和执行多个进程, 并提供接口来提交任务、获取任务结果等操作。

        在使用进程池是, 开发者可以将需要执行的任务提交给进程池,进程池会自动分配可用的进程来执行任务。这样可以避免重复创建和销毁进程的开销,提高应用程序的性能和效率。

进程池的特点:

  • 可以预先创建一定数量的进程,减少创建和销毁进程的开销。

  • 提高接口来提交任务, 并将任务分配给空闲的进程来执行。

  • 可以限制同时执行的任务数量,以控制并发。

  • 提供接口来获取任务的执行的结果。

构造方法:

Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])

  • processes:指定进程池中的进程数量,默认为 None,表示使用系统的 CPU 核心数;

  • initializer:一个可调用对象,用于在每个子进程启动时调用,用于初始化子进程;

  • initargs:一个元组,作为 initializer 函数的参数传递给子进程;

  • maxtasksperchild:指定每个子进程最多执行的任务数,默认为 None,表示不限制任务数;

  • context:指定一个上下文对象,用于创建子进程,默认为 None,表示使用默认的上下文对象。

进程池的实例方法:

  • apply(func, args=(), kwds={}):阻塞地提交一个任务,等待任务完成并返回结果。

    • func 是要执行的函数;

    • args 是传递给函数的位置参数,必须是一个元组;

    • kwds 是传递给函数的关键字参数,必须是一个字典。

  • map(func, iterable, chunksize=None):将一个可迭代对象中的所有元素应用到同一个函数上,并返回结果列表,按照可迭代对象中元素的顺序排列。

    • func 是要执行的函数;

    • iterable 是要处理的可迭代对象;

    • chunksize 是每个子进程处理的任务数量,默认为 None,表示使用默认的块大小。

  • imap(func, iterable, chunksize=None):与 map() 方法类似,但是它返回一个迭代器,可以一次获取一个结果。

    • func 是要执行的函数;

    • iterable 是要处理的可迭代对象;

    • chunksize 是每个子进程处理的任务数量,默认为 None,表示使用默认的块大小。

# 实例方法
import multiprocessing
​
def run(x):return x ** 2 ,f'跑了这些时间!'
​
if __name__ == '__main__':with multiprocessing.Pool(processes=4) as pool:res1 = pool.apply(run,(10,))
​res2 = pool.map(run,range(10))
​res3 = pool.imap(run, range(10))
​
​for i in res3:print(i)# (0, '跑了这些时间!')
# (1, '跑了这些时间!')
# (4, '跑了这些时间!')
# (9, '跑了这些时间!')
# (16, '跑了这些时间!')
# (25, '跑了这些时间!')
# (36, '跑了这些时间!')
# (49, '跑了这些时间!')
# (64, '跑了这些时间!')
# (81, '跑了这些时间!')

异步提交:

import multiprocessing
​
def my_func(arg):# 进程要执行的任务pass
​
if __name__ == "__main__":# 获取Context对象context = multiprocessing.get_context()
​# 创建进程池with context.Pool(4) as pool:# 异步提交任务给进程池result = pool.apply_async(my_func, (1,))# 阻塞主进程等待任务完成output = result.get()print(output)

同步提交:

import multiprocessing
​
def my_func(arg):# 进程要执行的任务pass
​
if __name__ == "__main__":# 获取Context对象context = multiprocessing.get_context()
​# 创建进程池with context.Pool(4) as pool:# 同步提交任务给进程池output = pool.apply(my_func, (1,))print(output)

【5】协程

综上所述:线程才是执行单位

协程的基础使用

        这是 python 3.7 里面的基础协程用法,现在这种用法已经基本稳定,不太建议使用之前的语法了。

【1】yield关键字

  • yield可以保存状态

    • yield的状态保存与操作系统的保存线程状态很像,但是yield是代码级别控制的,更轻量级。

  • send可以把一个函数的结果传给另一个函数

    • 以此实现单线程内程序之间的切换。

注意:单纯地切换反而会降低运行效率。

【2】创建协程的两种方法

生成器

def my_coroutine():while True:value = yield  # 接收从调用方传递过来的值print('收到:', value)​
# 创建生成器对象
coroutine = my_coroutine()​
# 启动生成器
next(coroutine)​
# 发送数据到生成器
coroutine.send('Hello')
coroutine.send('World')​
# 关闭生成器
coroutine.close()

asyncio库

import asyncio
​
async def my_coroutine():print('coroutine started')await asyncio.sleep(1)print('coroutine ended')
​
asyncio.run(my_coroutine())  # 使用 asyncio.run() 运行协程
# coroutine started
# coroutine ended

并发、并行、同步和异步

并发指的是 一个 CPU 同时处理多个程序,但是在同一时间点只会处理其中一个。并发的核心是:程序切换。

但是因为程序切换的速度非常快,1 秒钟内可以完全很多次程序切换,肉眼无法感知。

bingfa.jpg

并行指的是多个 CPU 同时处理多个程序,同一时间点可以处理多个。

并行.jpg

同步:执行 IO 操作时,必须等待执行完成才得到返回结果。 异步:执行 IO 操作时,不必等待执行就能得到返回结果。

yibu.jpg

协程,线程和进程的区别

        多进程通常利用的是多核 CPU 的优势,同时执行多个计算任务。每个进程有自己独立的内存管理,所以不同进程之间要进行数据通信比较麻烦。

        多线程是在一个 cpu 上创建多个子任务,当某一个子任务休息的时候其他任务接着执行。多线程的控制是由 python 自己控制的。 子线程之间的内存是共享的,并不需要额外的数据通信机制。但是线程存在数据同步问题,所以要有锁机制。

        协程的实现是在一个线程内实现的,相当于流水线作业。由于线程切换的消耗比较大,所以对于并发编程,可以优先使用协程。

协程的主要使用场景

协程的主要应用场景是 IO 密集型任务,总结几个常见的使用场景:

  • 网络请求,比如爬虫,大量使用 aiohttp
  • 文件读取, aiofile
  • web 框架, aiohttp, fastapi
  • 数据库查询, asyncpg, databases

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

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

相关文章

铭文:探索比特币世界的数字印记

铭文是什么? 铭文指的是在某种物品(如石头、硬币、平板等)上刻有文字。在比特币领域,铭文指的是刻在聪(satoshi)上的元数据。比特币的最小单位是聪,1比特币可分为1亿聪。每个聪都通过序数理论进…

OpenAI GPT LLMs 高级提示词工程方法汇总

原文地址:An Introduction to Prompt Engineering for OpenAI GPT LLMs Github:Prompt-Engineering-Intro 2023 年 3 月 2 日 提示工程指南 | Prompt Engineering Guide Naive 提示词:带有提示的情感分类器 prompt Decide whether a T…

计算机缺失iutils.dll怎么办,分享5种靠谱的解决方法

​在计算机系统运行过程中,如果发现无法找到或缺失iutils.dll文件,可能会引发一系列的问题与故障。首先,由于iutils.dll是系统中一个重要的动态链接库文件,它的主要功能可能涉及到系统核心服务、应用程序支持或者特定功能模块的运…

互联网高频面:输入URL按下回车后,中间发生了什么

题目 输入URL按下回车后,中间发生了什么 这个问题其实是计算机网络里面很经典的一个问题,不能去死机硬背,很考察对网络架构和通信原理的理解,也是各个互联网大厂喜欢考察的面试题。 一些图片参考了小林的计算机网络面经 从输入…

“光谱视界革新:ChatGPT在成像光谱遥感中的智能革命“

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境,是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型,在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用,人工智能…

爬虫案例2:playwright 超爽体验

参考链接:https://playwright.bootcss.com/python/docs/intro 目标网站:https://spa6.scrape.center/通过观察,页面的信息是通过Ajax请求后返回的信息 下面使用playwright实现绕过token的获取直接拿到返回的数据import asyncio import json f…

深入挖掘C语言之——联合

目录 联合的定义 联合的特点 联合的应用场景 在C语言中,联合(Union)是一种特殊的数据结构,它允许在同一内存地址存储不同类型的数据。与结构体(Struct)不同的是,联合中的所有成员共享同一块内…

C语言--从零开始的扫雷游戏

C语言--从零开始的扫雷游戏 1. 游戏说明2. 总体代码3. 详细讲解3.1 菜单部分3.2 游戏主体部分3.2.1 总体分析3.2.2 棋盘初始化3.2.3 棋盘展示3.2.4 设置地雷3.2.5 扫雷阶段3.2.6 统计雷个数的代码3.2.7 使用迭代的方式进行展开:3.2.8 扫雷部分主体代码 4. 总结 1. 游…

docker常用操作-docker私有仓库的搭建(Harbor),并将本地镜像推送至远程仓库中。

1、docker-compose安装,下载docker-compose的最新版本 第一步:创建docker-compose空白存放文件vi /usr/local/bin/docker-compose 第二步:使用curl命令在线下载,并制定写入路径 curl -L "https://github.com/docker/compos…

Linux学习:基础开发工具的使用(1)

目录 1. Linux软件包管理器:yum工具1.1 yum是什么(软件商城)1.2 yum的使用1.3 yum的背景生态 2. 项目开发与集成开发环境3. vim编辑器3.1 vim编辑器的常见模式与模式切换3.3 vim编辑器的使用3.3.1 命令模式下的常见命令:3.3.2 vim…

【rk3368 android6.0 恢复出厂设置功能】

rk3368 android6.0 恢复出厂设置功能 恢复出厂设置三种方法一,设置--进入恢复出厂设置页面二,发送广播形式三,命令形式总结 郑重声明:本人原创博文,都是实战,均经过实际项目验证出货的 转载请标明出处:攻城狮2015 恢复…

主机、虚拟机和开发板三者互相之间能ping通的配置参数

主机网络配置 开发板网络配置 虚拟机网络配置

OKHttpRetrofit

完成一个get请求 1.导入依赖 implementation("com.squareup.okhttp3:okhttp:3.14.")2.开启viewBinding android.buildFeatures.viewBinding true 3.加网络权限 和 http明文请求允许配置文件 <?xml version"1.0" encoding"utf-8"?> &l…

在受污染的二叉树中查找元素(Lc1261)——DFS+哈希表

给出一个满足下述规则的二叉树&#xff1a; root.val 0如果 treeNode.val x 且 treeNode.left ! null&#xff0c;那么 treeNode.left.val 2 * x 1如果 treeNode.val x 且 treeNode.right ! null&#xff0c;那么 treeNode.right.val 2 * x 2 现在这个二叉树受到「污染…

数据结构:详解【顺序表】的实现

1. 顺序表的定义 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。动态顺序表与数组的本质区别是——根据需要动态的开辟空间大小。 2. 顺序表的功能 动态顺序表的功能一般有如下几个&#xff1a; 初始化顺序表打印顺序…

腾讯云轻量服务器地域怎么选择?上海/北京/广州哪个合适?

腾讯云轻量应用服务器地域如何选择&#xff1f;地域就近选择&#xff0c;北方选北京地域、南方选广州地域&#xff0c;华东地区选上海地域。广州上海北京地域有什么区别&#xff1f;哪个好&#xff1f;区别就是城市地理位置不同&#xff0c;其他的差不多&#xff0c;不区分好坏…

【C++教程从0到1入门编程】第九篇:STL中Vector类

一、vector的介绍 1.vector的介绍 vector是表示可变大小数组的序列容器。 就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以动态改变的&…

G6.antv自定义箭头 懒加载数据 箭头丢失

懒加载数据会导致箭头消失&#xff0c;只能自定义箭头 层次图&#xff1a;dagre 折线&#xff1a;polyline 设置endArrow&#xff1a;true 接口懒加载数据&#xff0c;执行 this.graph.read(this.graphData); this.graph.zoom(0.6);箭头消失&#xff0c;需自定义箭头 空心箭头…

香港理工大学主办!2024年第八届电力能源系统与应用国际会议(ICoPESA 2024)即将召开!

2024年第八届电力能源系统与应用国际会议&#xff08;ICoPESA 2024&#xff09; 2024年6月24日-26日 中国香港 ICoPESA 2024-Hong Kong (icpesa.org)https://icpesa.org/index.html 会议组织单位 会议出版及检索&#xff1a; 会议录用并注册的论文将由IEEE出版&#xff0c;…

[BJDCTF2020]Cookie is so stable

hint提示查看cookies flag.php页面我们先随便输入一个名字 输入后我们重新进一次flag.php&#xff0c;发现cookie里存储了刚刚登陆时输入的用户名&#xff0c;直接猜是ssti 尝试后根据ssti特征判断是twig模板 {{_self.env.registerUndefinedFilterCallback("exec")…