Python的多进程、多线程、协程 —— 进程和线程 前言 Python的多进程、多线程、协程 —— 进程和线程 Python的多进程、多线程、协程 —— 协程 Python的多进程、多线程、协程 —— join的用法 主体 本文主要从多进程、多线程、协程的概念入手,介绍他们的区别以及多进程和多线程的一些简单代码案例 何为进程、线程、协程 进程:运行起来的程序就是进程,是操作系统分配资源的最小单位。线程:线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。协程:是线程的更小切分,又称为“微线程”,是一种用户态的轻量级线程。三者关系:进程里有线程,线程里有协程 进程、线程、协程的区别 进程:针对于python语言执行环境来说,多进程是利用多核CPU来完成任务,进程拥有独立的内存空间,所以进程间数据不共享,进程之间的通讯是由操作系统完成的,在切换时,CPU需要进行上下文切换,导致通讯效率比较低、开销比较大。线程:多线程是在一个进程内运行,共享进程的内存空间,通讯效率较高、开销较小。但缺点是:因为python底层的GIL锁(global interpeter lock),python的底层的同步锁太多导致多线程被同步很多次,搞成并发效果不佳。协程:一个可以挂起的函数,协程的调度完全由用户控制,用函数切换,开销极小。 多进程、多线程、协程的使用场景 多进程:CPU密集运算,大部分时间花在计算多线程、协程:IO密集型(网络IO、磁盘IO、数据库IO),大部分时间花在传输 多进程的简单案例 进程池和map的使用 进程 类的使用 使用join,主进程跑到join那里,处于挂起状态,看源码,join可以通过设置timeout来决定主进程等待多长时间,若子进程在timeout用完了还没跑完,主进程会杀死子进程。 在进程之间交换对象 支持进程之间的两种通信通道: 队列 类是一个近似 的克隆 队列是线程和进程安全的。 管道 函数返回一个由管道连接的连接对象,默认情况下是双工(双向) 进程间同步 包含来自 的所有同步原语的等价物。例如,可以使用锁来确保一次只有一个进程打印到标准输出: 不使用锁的情况下,来自于多进程的输出很容易产生混淆。 进程间共享状态 如上所述,在进行并发编程时,通常最好尽量避免使用共享状态。使用多个进程时尤其如此。 但是,如果你真的需要使用一些共享数据,那么 提供了两种方法。 共享内存 可以使用 或 将数据存储在共享内存映射中。例如,以下代码: 创建 和 时使用的 和 参数是 模块使用的类型的 typecode : 表示双精度浮点数, 表示有符号整数。这些共享对象将是进程和线程安全的。 为了更灵活地使用共享内存,可以使用 模块,该模块支持创建从共享内存分配的任意ctypes对象。 服务进程 由 返回的管理器对象控制一个服务进程,该进程保存Python对象并允许其他进程使用代理操作它们。 返回的管理器支持类型: 、 、 、 、 、 、 、 、 、 、 、 和 。例如 进程池 可以创建一个进程池,它将使用 类执行提交给它的任务。 class multiprocessing.pool.Pool([processes[,initializer[,initargs[,maxtasksperchild[,context]]]]]) 一个进程池对象,它控制可以提交作业的工作进程池。它支持带有超时和回调的异步结果,以及一个并行的 map 实现。processes 是要使用的工作进程数目。如果 processes 为 ,则使用 返回的值。initializer 不为 ,则每个工作进程将会在启动时调用 。maxtasksperchild 是一个工作进程在它退出或被一个新的工作进程代替之前能完成的任务数量,为了释放未使用的资源。默认的 maxtasksperchild 是 ,意味着工作进程寿与池齐。context 可被用于指定启动的工作进程的上下文。通常一个进程池是使用函数 或者一个上下文对象的 方法创建的。在这两种情况下, context 都是适当设置的。 注意,进程池对象的方法只有创建它的进程能够调用。警告 对象具有需要正确管理的内部资源 (像任何其他资源一样),具体方式是将进程池用作上下文管理器,或者手动调用 和 。 未做此类操作将导致进程在终结阶段挂起。请注意依赖垃圾回收器来销毁进程池是 不正确的 做法,因为 CPython 并不保证进程池终结器会被调用(请参阅 来了解详情)。备注 通常来说, 中的 Worker 进程的生命周期和进程池的工作队列一样长。一些其他系统中(如 Apache, mod_wsgi 等)也可以发现另一种模式,他们会让工作进程在完成一些任务后退出,清理、释放资源,然后启动一个新的进程代替旧的工作进程。 的 maxtasksperchild 参数给用户提供了这种能力。apply_async(func[,args[,kwds[,callback[,error_callback]]]]): 方法的一个变种,返回一个 对象。如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。回调函数应该立即执行完成,否则会阻塞负责处理结果的线程。map(func,iterable[,chunksize]):内置 函数的并行版本 (但它只支持一个 iterable 参数,对于多个可迭代对象请参阅 )。 它会保持阻塞直到获得结果。这个方法会将可迭代对象分割为许多块,然后提交给进程池。可以将 chunksize 设置为一个正整数从而(近似)指定每个块的大小可以。注意对于很长的迭代对象,可能消耗很多内存。可以考虑使用 或 并且显式指定 chunksize 以提升效率。map_async(func,iterable[,chunksize[,callback[,error_callback]]]): 方法的一个变种,返回一个 对象。如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。回调函数应该立即执行完成,否则会阻塞负责处理结果的线程。close():阻止后续任务提交到进程池,当所有任务执行完成后,工作进程会退出。terminate():不必等待未完成的任务,立即停止工作进程。当进程池对象被垃圾回收时,会立即调用 。join():等待工作进程结束。调用 前必须先调用 或者 。get([timeout]):用于执行结果。如果 timeout 不是 并且在 timeout 秒内仍然没有执行完得到结果,则抛出 异常。如果远程调用发生异常,这个异常会通过 重新抛出。wait([timeout]):阻塞,直到返回结果,或者 timeout 秒后超时。ready():返回执行状态,是否已经完成。successful():判断调用是否已经完成并且未引发异常。 如果还未获得结果则将引发 。在 3.7 版更改: 如果没有执行完,会抛出 异常而不是 。 多线程的简单案例 线程可以直接使用全局变量作为共享数据,但是在使用的时候需要添加线程锁 线程池的用法多线程可以并行处理多个任务,但开启线程不仅花费时间,也需要占用系统资源。因此,线程数量不是越多越快,而是要保持在合理的水平上。线程池可以让我们用固定数量的线程完成比线程数量多得多的任务。with 线程池默认是等待子任务结束 多进程、多线程使用场景案例 怎么样去理解多进程适合计算类型而多线程适合i/o类型呢?请看下面的代码案例,更能直观的展示结果。 多进程案例 结果如图
多线程案例 结果如图
后记 如果本文对你有所帮助,还请点个赞~~
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/79162.html