利用 Python 实现多任务进程

eff60fb617601e0bc0112a2624a980dc.gif

来源:杰哥的IT之旅
作者:阿拉斯加

一、进程介绍

进程:正在执行的程序,由程序、数据和进程控制块组成,是正在执行的程序,程序的一次执行过程,是资源调度的基本单位。

程序:没有执行的代码,是一个静态的。

二、线程和进程之间的对比

7a10d930fbfaaa57cec0406f647a0f49.png

由图可知:此时电脑有 9 个应用进程,但是一个进程又会对应于多个线程,可以得出结论:

进程:能够完成多任务,一台电脑上可以同时运行多个 QQ

线程:能够完成多任务,一个 QQ 中的多个聊天窗口

根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位.

使用多进程的优势:

1、拥有独立GIL:

首先由于进程中 GIL 的存在,Python 中的多线程并不能很好地发挥多核优势,一个进程中的多个线程,在同 一时刻只能有一个线程运行。而对于多进程来说,每个进程都有属于自己的 GIL,所以,在多核处理器下,多进程的运行是不会受 GIL的影响的。因此,多进 程能更好地发挥多核的优势。

2、效率高

当然,对于爬虫这种 IO 密集型任务来说,多线程和多进程影响差别并不大。对于计算密集型任务来说,Python 的多进程相比多线 程,其多核运行效率会有成倍的提升。

f27426caa9e9a600f2871e5c276e3447.png

三、Python 实现多进程

我们先用一个实例来感受一下:

1、使用 process 类

import multiprocessing 
def process(index): print(f'Process: {index}') 
if __name__ == '__main__': for i in range(5): p = multiprocessing.Process(target=process, args=(i,)) p.start()

这是一个实现多进程最基础的方式:通过创建 Process 来新建一个子进程,其中 target 参数传入方法名,args 是方法的参数,是以 元组的形式传入,其和被调用的方法 process 的参数是一一对应的。

注意:这里 args 必须要是一个元组,如果只有一个参数,那也要在元组第一个元素后面加一个逗号,如果没有逗号则 和单个元素本身没有区别,无法构成元组,导致参数传递出现问题。创建完进程之后,我们通过调用 start 方法即可启动进程了。

运行结果如下:

Process: 0 
Process: 1 
Process: 2 
Process: 3 
Process: 4

可以看到,我们运行了 5 个子进程,每个进程都调用了 process 方法。process 方法的 index 参数通过 Process 的 args 传入,分别是 0~4 这 5 个序号,最后打印出来,5 个子进程运行结束。

2、继承 process 类

from multiprocessing import Process
import timeclass MyProcess(Process):def __init__(self,loop):Process.__init__(self)self.loop = loopdef run(self):for count in range(self.loop):time.sleep(1)print(f'Pid:{self.pid} LoopCount: {count}')
if __name__ == '__main__':for i in range(2,5):p = MyProcess(i)p.start()

我们首先声明了一个构造方法,这个方法接收一个 loop 参数,代表循环次数,并将其设置为全局变量。在 run方法中,又使用这 个 loop 变量循环了 loop 次并打印了当前的进程号和循环次数。

在调用时,我们用 range 方法得到了 2、3、4 三个数字,并把它们分别初始化了 MyProcess 进程,然后调用 start 方法将进程启动起 来。

注意:这里进程的执行逻辑需要在 run 方法中实现,启动进程需要调用 start 方法,调用之后 run 方法便会执行。

运行结果如下:

Pid:12976 LoopCount: 0
Pid:15012 LoopCount: 0
Pid:11976 LoopCount: 0
Pid:12976 LoopCount: 1
Pid:15012 LoopCount: 1
Pid:11976 LoopCount: 1
Pid:15012 LoopCount: 2
Pid:11976 LoopCount: 2
Pid:11976 LoopCount: 3

注意,这里的进程 pid 代表进程号,不同机器、不同时刻运行结果可能不同。

四、进程之间的通信

1、Queue-队列 先进先出

from multiprocessing import Queue
import multiprocessingdef download(p): # 下载数据lst = [11,22,33,44]for item in lst:p.put(item)print('数据已经下载成功....')def savedata(p):lst = []while True:data = p.get()lst.append(data)if p.empty():breakprint(lst)def main():p1 = Queue()t1 = multiprocessing.Process(target=download,args=(p1,))t2 = multiprocessing.Process(target=savedata,args=(p1,))t1.start()t2.start()if __name__ == '__main__':main()
数据已经下载成功....
[11, 22, 33, 44]

2、共享全局变量不适用于多进程编程

import multiprocessinga = 1def demo1():global aa += 1def demo2():print(a)def main():t1 = multiprocessing.Process(target=demo1)t2 = multiprocessing.Process(target=demo2)t1.start()t2.start()if __name__ == '__main__':main()

运行结果:

1

有结果可知:全局变量不共享;

五、进程池之间的通信

1、进程池引入

当需要创建的子进程数量不多时,可以直接利用 multiprocessing 中的 Process 动态生成多个进程,但是如果是上百甚至上千个目标,手动的去创建的进程的工作量巨大,此时就可以用到 multiprocessing 模块提供的 Pool 方法。

from multiprocessing import Pool
import os,time,randomdef worker(a):t_start = time.time()print('%s开始执行,进程号为%d'%(a,os.getpid()))time.sleep(random.random()*2)t_stop = time.time()print(a,"执行完成,耗时%0.2f"%(t_stop-t_start))if __name__ == '__main__':po = Pool(3)        # 定义一个进程池for i in range(0,10):po.apply_async(worker,(i,))    # 向进程池中添加worker的任务print("--start--")po.close()      po.join()       print("--end--")

运行结果:

--start--
0开始执行,进程号为6664
1开始执行,进程号为4772
2开始执行,进程号为13256
0 执行完成,耗时0.18
3开始执行,进程号为6664
2 执行完成,耗时0.16
4开始执行,进程号为13256
1 执行完成,耗时0.67
5开始执行,进程号为4772
4 执行完成,耗时0.87
6开始执行,进程号为13256
3 执行完成,耗时1.59
7开始执行,进程号为6664
5 执行完成,耗时1.15
8开始执行,进程号为4772
7 执行完成,耗时0.40
9开始执行,进程号为6664
6 执行完成,耗时1.80
8 执行完成,耗时1.49
9 执行完成,耗时1.36
--end--

一个进程池只能容纳 3 个进程,执行完成才能添加新的任务,在不断的打开与释放的过程中循环往复。

2fd8b55243974347aee39e0b92632c75.png

六、案例:文件批量复制

操作思路:

  • 获取要复制文件夹的名字

  • 创建一个新的文件夹

  • 获取文件夹里面所有待复制的文件名

  • 创建进程池

  • 向进程池添加任务

代码如下:

导包

import multiprocessing
import os
import time

定制文件复制函数

def copy_file(Q,oldfolderName,newfolderName,file_name):# 文件复制,不需要返回time.sleep(0.5)# print('\r从%s文件夹复制到%s文件夹的%s文件'%(oldfolderName,newfolderName,file_name),end='')old_file = open(oldfolderName + '/' + file_name,'rb') # 待复制文件content = old_file.read()old_file.close()new_file = open(newfolderName + '/' + file_name,'wb') # 复制出的新文件new_file.write(content)new_file.close()Q.put(file_name) # 向Q队列中添加文件

定义主函数

def main():oldfolderName = input('请输入要复制的文件夹名字:') # 步骤1获取要复制文件夹的名字(可以手动创建,也可以通过代码创建,这里我们手动创建)newfolderName = oldfolderName + '复件'# 步骤二 创建一个新的文件夹if not os.path.exists(newfolderName):os.mkdir(newfolderName)filenames = os.listdir(oldfolderName) # 3.获取文件夹里面所有待复制的文件名# print(filenames)pool = multiprocessing.Pool(5) # 4.创建进程池Q = multiprocessing.Manager().Queue() # 创建队列,进行通信for file_name in filenames:pool.apply_async(copy_file,args=(Q,oldfolderName,newfolderName,file_name)) # 5.向进程池添加任务po.close()copy_file_num = 0file_count = len(filenames)# 不知道什么时候完成,所以定义一个死循环while True:file_name = Q.get()copy_file_num += 1time.sleep(0.2)print('\r拷贝进度%.2f %%'%(copy_file_num  * 100/file_count),end='') # 做一个拷贝进度条if copy_file_num >= file_count:break

程序运行

if __name__ == '__main__':main()

运行结果如下图所示:

667d3bbe0da35203a6366c839ce23718.png

运行前后文件目录结构对比

运行前

13c3dab621c44907decd75f3dbdc65e9.png

运行后

399a6944a9b4641aee04da699a506ebb.png

以上内容就是整体大致结果了,由于 test 里面是随便粘贴的测试文件,这里就不展开演示了。

349c0cc476d10939fc4d0ac125f652a8.gif

8b49ee33b8cd1cceea8e471eabe50146.png

往期推荐

Facebook 遭遇史诗级故障!

数学在左,人生在右

Redis很厉害,使用规范来啦

携手开发者,一起精准打造数智未来!

bcadc8bec1dc04eedfdb119a42f642cd.gif

点分享

a7983137776c97c307a43d37ccba79d1.gif

点收藏

606e3a99e2c059b560d46042670e98d9.gif

点点赞

07d3e21eaf5a9b5d0d01a693546dfcc1.gif

点在看

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

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

相关文章

小白也能懂的 Nacos 服务模型介绍

简介: 理解了 Nacos 的服务模型,也有利于我们了解 Nacos 背后的工作原理,从而确保我们正确地使用 Nacos。 作者:岛风 前言 按照目前市场上的主流使用场景,Nacos 被分成了两块功能:服务注册发现&#xff0…

那些与 IE 相伴的日子

来源:零一作者:前端印象大家好,IE 大家都不陌生,毕竟出现在大家的视野中已经很久很久,久到有20多年,当然也因前端技术的快速更新,给需要兼容IE浏览器的前端程序员带来了不少的困扰。慢慢地&…

html代码style图片width,HTML Style columnWidth用法及代码示例

DOM中的columnWidth属性用于指定列的宽度。用法:返回columnWidth属性:object.style.columnWidth设置columnWidth属性:object.style.columnWidth "auto | length | initial | inherit"属性值:auto:缺省值。列宽将由浏览器确定lengt…

KubeVela 1.0 :开启可编程式应用平台的未来

简介: 如果你对云原生领域不太关注,可能对 KubeVela 还没有做过太深入的了解。别着急,本文就借着 v1.0 发布之际,为你详细的梳理一次 KubeVela 项目的发展脉络,解读它的核心思想和愿景,领悟这个正冉冉升起的…

android-x86 镜像iso下载_2019年微软MSDN原版镜像系统下载地址 Win10/7原版系统iso镜像文件...

如今,不少用户开始讨厌以GHOST形式来安装操作系统,虽然步骤十分简单,但是从网上下载的GHOST系统,已经形成了一个黑色产业链,为了盈利,捆绑了软件全家桶、恶意强制主页,甚至捆绑木马,…

大流量场景下如何云淡风轻地进行线上发布?

简介: 本文介绍了微服务治理下金丝雀发布的能力,解决了发布期间少量流量验证新功能的问题。 前言 本文,我们继续聊聊《揭秘大流量场景下发布如丝般顺滑背后的原因》中的另外一环,灰度发布,也叫金丝雀发布。 ​ 很多互…

匿名提问:rm -rf了怎么办?

整理 | 易璜珵出品 | 《新程序员》IT界流传着一个神秘的代码,老程序员听了总是意味深长地一笑,而新手程序员则总是手痒地想试试,那就是删库指令rm -rf。这一行代码下去,海量数据可能就荡然无存。近几年发生的“删库跑路”事件让这…

ubuntu matlab_有没有人和我一起整理Python的matlab代替

想找人一起整理Python中matlab代替的包,最好是能够将常用功能用tkinter封装起来,积少成多,逐步逼近完整。比如将scipy中的最小二乘法拟合功能,找个图形界面封装一下,就变成了曲线拟合工具,可以代替matlab的…

逸仙电商Seata企业级落地实践

简介: 本文将会以逸仙电商的业务作为背景, 先介绍一下seata的原理, 并给大家进行线上演示, 由浅入深去介绍这款中间件, 以便读者更加容易去理解 Seata 这个中间件。 作者 | 张嘉伟(GitHub ID:l…

“类云”的存储服务什么样?Pure Storage发布Pure Fusion等系列新品

一键部署自动化存储平台与云原生数据库即服务,无缝连接基础设施运营与应用程序。 编辑 | 宋慧 出品 | CSDN 云计算 近日,专为多云环境提供存储即服务的Pure Storage发布一系列现代化基础设施、运营及应用程序,这是Pure Storage迈向创新现代…

mac mongodb可视化工具_MongoDB从立地到成佛(介绍、安装、增删改查)

文章作者公众号bigsai,已收录在回车课堂,如有帮助还请不吝啬点个赞赞支持一下!课程导学大家好我是bigsai,我们都学过数据库,但你可能更熟悉关系(型)数据库例如MySQL,SQL SERVER,ORACLE等,对于非…

阿里巴巴云原生 etcd 服务集群管控优化实践

简介: 这些年,阿里云原生 etcd 服务发生了翻天覆地的变化,这篇文章主要分享一下 etcd 服务在面对业务量大规模增长下遇到的问题以及我们是如何解决的,希望对读者了解 etcd 的使用和管控运维提供经验分享。 作者 | 陈星宇&#xff…

计算机组装与维护思考问题,计算机组装与维护中的常见问题及解决方法

郜庆国摘要:在如今的社会下,各个领域的很多行业在工作的时候都需要用到计算机来帮助工作的进行,因为计算机在很多情况下都能够很好地进行计算与帮助,所以我们在进行工作时,不仅提高了工作的效率,还解决了很…

淘票票首次公开小程序开发秘籍,踩过坑才知道怎么走!

简介: 在2019年,阿里巴巴文娱的淘票票几乎涉足了当时市面上所有的小程序。在不少平台上,淘票票是阿里“第一批吃螃蟹”的技术团队。回顾过往,阿里文娱做过很多尝试,也踩过很多坑。《小程序 大世界》总结了淘票票过去 2…

stm32f407 6个串口dma_stm32之DMA

一. 对于大容量的STM32芯片有2个DMA控制器,控制器1有7个通道,控制器2有5个通道每个通道都可以配置一些外设的地址。二. 通道的配置过程:1. 首先设置CPARx寄存器和CMARx寄存器。通过DMA控制器把一个地址的值复制到另外一个地址,通过…

立足当下,塑造未来

今天,以“5G与世界同行”为主题的2021全球移动宽带论坛(Global MBB Forum)在迪拜举行。期间,华为轮值董事长胡厚崑发表了题为“立足当下,塑造未来”的主题演讲。胡厚崑指出:“5G预商用五年以来,…

jfinal html5,Jfinal框架整合webSocket技术功能实现

技术难度:简单在这里我会用最简单的方法实现JFinal框架结合webSocket最基础的功能,以至于后续业务的拓展需要小伙伴们依据实际情况去实现相应的开发!废话不多说,直接上代码!1、编写webSocket类package morality.ws;imp…

行业实战 | 5G+边缘计算+“自由视角” 让体育赛事更畅快

简介: 世界本是多维的。进入5G时代,观众对多维度视觉体验的需求日益增长,5G MEC网络与边缘计算的结合,具备大带宽、低延迟特性,使视频多维视觉呈现成为现实。在第二十三届CUBA中国大学生篮球联赛期间,中国电…

华为汪涛:走向智能世界2030,无线网络未来十年十大产业趋势

2021全球移动宽带论坛(Global MBB Forum)期间,华为常务董事、ICT基础设施业务管理委员会主任汪涛发表了题为“走向智能世界2030,无线网络未来十年十大趋势”的主题演讲。汪涛表示:“未来十年,是走向智能世界…

python怎么输入一个数字并调用_Python3 实例(一)

原标题:Python3 实例(一) Python Hello World 实例 以下实例为学习Python的第一个实例,即如何输出"Hello World!": 实例 # -*- coding: UTF-8 -*- # Filename : helloworld.py # author by : www.runoob.com…