张钊*,沈啸彬*, 王旭* 李月,曹海艳,
(淮北师范大学计算机科学与技术学院,淮北师范大学经济与管理学院,安徽 淮北)
*These authors contributed to the work equllly and should be regarded as co-first authors.
🌞欢迎来到python的世界
🌈博客主页:卿云阁💌欢迎关注🎉点赞👍收藏⭐️留言📝
🌟本文由卿云阁原创!
🌠本阶段属于练气阶段,希望各位仙友顺利完成突破
📆首发时间:🌹2022年12月1日🌹
✉️希望可以和大家一起完成进阶之路!
🙏作者水平很有限,如果发现错误,请留言轰炸哦!万分感谢!
目录
进程线程有多重要?刚开始学Python的时候你可能还没有感觉到,因为你写的代码从上到下 执行一遍就可以了,但实际上这很初级,实际开发写项目的时候,为了充分利用电脑配置来 加快程序进度,我们往往会用到多进程多线程。但是实话实说,博主学习这个的原因是因为项目的需要,我是做了一个项目,需要目标检测和测距并配合语音进行输出,一开始时我是把语音直接加在目标检测的后面,但是这产生了延迟的问题,为了解决这个问题,我开始学习这个知识点。🍈 多任务操作系统
操作系统可以执行多个任务,比如我们的Windows系统,除了目前在执行的、你能看得到的 几个任务,还有很多后台正在执行的任务,可以用Ctrl+Alt+Del键调出任务管理器看一下就知道了。我的电脑配置经常会看到有几核处理器的属性,例如我的电脑是8核的,也就是说电脑最多能同时执行8个任务,最多运行8个进程同时进行。但为什么我们的电脑却能够同时运行几百个任务呢?
其实这得益于于操作系统的任务调度,大部分的操作系统是采用抢占时间片的形式进行调度 。系统在极其微小的时间内,在多个任务之间进行极快速的切换,比如说8核的操作系统理论上1秒钟之内只能同时执行8个任务,但是系统在1秒钟之内可能在上百个任务之间进行切换,A任务执行一下、B任务执行一下、C任务执行一下......结果1秒钟之内很多任务都能被执行到,造成了肉眼可见的几百个任务在一直执行。 术语叫“宏观并行,微观串行”,实际上电脑在极端的时间内只能执行不超过配置核数的任 务数,8核还是只能执行8个任务。
🍉何为进程?何为线程?
进程就是任务,1个进程就相当于1个任务,是操作系统分配资源的最 小单位。在python中,想要实现多任务可以使用进程来完成,进程是实现多任务的一种方式。进程的多个子任务就称之为线程,线程是进程的最小执行单位, 一个进程可以有很多线程, 每个线程执行的任务都不一样。Python既支持多进程又支持多线程
🍊Python的多进程multiprocessing (包)
如果你利用多进程,你的Python代码是从头到尾逐行执行的,这其实就是在执行1个进程, 这一点应该很好理解。 要想更多利用CPU资源,我们可以利用多进程,这里介绍一个Python多进程时常用的包 multiprocessing,它拥有很多的功能,比如子进程、通讯、共享、执行不同的形式等等,我们来了解一些常用的。1.Process——进程类
Process是multiprocessing里面的一个进程类,通过它就能实现多进程。Process(target,name,args,kwargs)
- target是目标,在哪里新开进程让系统去执行?得给系统一个目标。
- name是进程的名字,你可以设置也可以不设置,默认是Process-N,N是从 1,2,3....N,系统默认从小到大取名。
- args和kwargs是参数,可用于传递到目标。
Process里面有很多方法,其中最常用的就是start()启动进程的方法。进程名.start() #开始进程
举例:写好的代码如下,我想看看开启和没开启多进程调用函数的效果。import time #2个要同时执行的函数 def music() : for i in range(5): #执行5次 print("听音乐中...") time.sleep(0.2) #延迟0.2s,目的是让效果对比更明显一些 def movie(): for i in range(5): print("看视频中...") time.sleep(0.2) #延迟0.2s music() movie() print("主进程执行完毕")
可以看到,这是很正常的运行情况,程序从上运行到下,逐行运行,music()里面的三次循环没有执行完毕就不会执行movie()里面,以及这两个函数如果没有执行完毕,就不会执行最后一行的print("主进程执行完毕")。我们再来看在上面案例的代码中加入多进程:import time import multiprocessing # 2个要同时执行的函数 def music(): for i in range(5): # 执行5次 print("听音乐中...") time.sleep(0.2) # 延迟0.2s,目的是让效果对比更明显一些 def movie(): for i in range(5): print("看视频中...") time.sleep(0.2) # 延迟0.2s if __name__ == "__main__": # 解决Windows系统下调用包时的递归问题 # 创建子进程 music_process = multiprocessing.Process(target=music) movie_process = multiprocessing.Process(target=movie) # 启用进程 music_process.start() movie_process.start() print("主进程执行完毕")
运行效果:可以看出来,这开启进程之后,代码运行时是有3个进程同时进行的,一个是从上往下执行的主进程,执行到下面输出“主进程执行完毕”,另外两个子进程去执行music()和movie()进程,从他们的执行速度来看,它们是同时在进行的,所以没有像刚才那样非要等其中一个函数里面的代码执行3遍才开始第2个函数。
🍋获取当前进程的编号
前面我们讲到了代码执行时有多个进程在同时进行任务,那么怎么样查看当前进程的编号来得知目前有哪些进程在运行呢?哪些是主进程哪些是子进程呢?3个方法,我们先来看一下方 法,后面再结合例子一起使用。(1)获取当前进程的编号:需要用到一个os模块里面的getpid()方法,用法如下:os.getpid()
(2)获取当前进程的名字这里用的还是multiprocessing包,里面有个current_process()的方法,用法如下:multiprocessing.current_process()
(3)获取当前父进程(主进程)的编号子进程是属于哪个父进程的?这个用的是os模块里面的getppid() ,用法如下:os.getppid()
那么方法都看到了,我们来在刚才的例子的基础上,获取并打印一下当前进程的名字、编号以及父进程的编号。运行结果:
🍔多线程Threading模块
多进程能同时运行几个任务,前面我们讲过进程的最小单位是线程,那么线程也同样可以进行多个任务。如果一个进程只有1个任务(主进程),那么也可以说是只有1个线程,就比如我们不使用多进程运行代码的时候,这时候就可以说1个主进程或1个主线程。1.多线程的类Thread类
多线程常用的一个模块是threading,里面有个教Thread的类,跟前面我们将多进程时用到的Process类差不多,我们先来看看用法:Thread(target=None,name=None,args=(),kwargs=None)
- target:可执行目标
- name:线程的名字默认Thread-N
- args/kwargs:目标参数
同样的,多线程也要有开启的方法,跟前面的也差不多:start()
还有获取线程名字的方法:threading.current_thread()
知道了这些知识点,我们开始举例:用跟上面差不多的例子去使用一下我们的多线程。import threading,time def music(name,loop): for i in range(loop): print("听音乐 %s , 第%s次"%(name,i)) time.sleep(0.2) def movie(name,loop): for i in range(loop): print("看电影%s , 第%s次"%(name,i)) time.sleep(0.2) if __name__ =="__main__": music_thread = threading.Thread(target=music,args=("最亲的人",3)) movie_thread = threading.Thread(target=movie,args=("唐探2",3)) music_thread.start() movie_thread.start() print("主线程执行完毕")
可以看出来,我们的多线程其实是跟多进程差不多的,同样可以运行多个任务,这里我们还增加了参数的使用。
🍿继承Thread类
我们除了用上面的方法实现多线程任务,还可以用继承类的方式去实现多线程。举例:通过多线程的方式,去打印“凉凉”和“头发没了"。MyThread这个类是我们自己创建的,它是继承于父类threading.Threadimport threading,time #多线程的创建 class MyThread(threading.Thread): def __init__(self,name): #初始化 super().__init__() #调用父类Thread的初始化方法 self.name = name #name变成实例属性 def run(self): #线程要做的事情 for i in range(5): print(self.name) time.sleep(0.2) #实例化子线程 t1 = MyThread("凉凉") t2 = MyThread("头发没了") t1.start() t2.start()
运行结果:随机效果是有的,你们的效果和我的可能会不一样,每台电脑在运行多线程代码时,哪个线程能够抢到时间片谁就先执行。 通过类Thread继承一样可以实现多线程。
🥓python提供了多种进程通信
一般来说进程之间是并行的,在实际的工程中时,常常是两个进程之间是有一定关系的,比如我现在需要完成任务一(进程1——>进程2进行加工和处理),所以必须然后让进程之间通信,此时我们必须要搭建一个桥梁,这个时候我就需要用到数据结构,队列。
再比如博主做的项目中,语音播报模块需要等目标检测模块中的值,这个时候我们应该怎么办呢?这里就涉及到进程的通信。python提供了多种进程通信的方式,主要Queue和Pipe这两种方式,Queue用于多个进程间实现通信,Pipe是两个进程的通信。
Queue有两个方法:
Put方法:以插入数据到队列中,他还有两个可选参数:blocked和timeout。
Get方法:从队列读取并且删除一个元素。同样,他还有两个可选参数:blocked和timeout。
from multiprocessing import Process, Queue, set_start_method import time,random,os #创建队列,容量为3 q=Queue(3) q.put(1) q.put(2) q.put(3) q.put(4,timeout=3)#一直没执行,意思是等待3秒。
from multiprocessing import Process, Queue, set_start_method import time,random,os #创建队列,容量为3 q=Queue(3) q.put(1) q.put(2) q.put(3) print(q.qsize()) while True: if not q.empty(): print(q.get()) else: print("该队列已为空") break
举个栗子:
比如现在我有一个任务是下载音乐然后保存音乐:
from multiprocessing import Process, Queue, set_start_method import time,random,os #下载音乐 def load_music(q): music_list=["1","2","3","4","5"] for i in music_list: print('正在下载<{}>音乐'.format(i)) q.put(i)#把下载好的音乐存入队列 #保存音乐 def save_music(q): while True: if not q.empty(): print('把<{}>音乐存入D盘'.format(q.get())) else: print("该队列已为空") break if __name__ == '__main__': q=Queue(4) p1 = Process(target=load_music,name='目标检测',args=(q,)) p2 = Process(target=save_music,name='保存音乐',args=(q,)) #启动子进程,写入 p1.start() p2.start()
再举一个我项目中的例子:
from multiprocessing import Process, Queue, set_start_method import time,random,os #目标检测 def load(q1,q2): state_list=["red","green"] dis_list=["1", "2", "3", "4", "5"] for i in state_list: print('检测当前的状态<{}>'.format(i)) q1.put(i)#把当前的状态存入队列 for i in dis_list: print('检测当前的距离<{}>'.format(i)) q2.put(i)#把当前的状态存入队列 #语音播报 def save(q1,q2): while True: if not q1.empty(): print('把状态<{}>语音播报'.format(q1.get())) else: print("该队列已为空") break while True: if not q2.empty(): print('把距离<{}>语音播报'.format(q2.get())) else: print("该队列已为空") break if __name__ == '__main__': q1=Queue(2) q2=Queue(5) p1 = Process(target=load,name='目标检测',args=(q1,q2,)) p2 = Process(target=save,name='语音播报',args=(q1,q2,)) #启动子进程,写入 p1.start() p2.start()
结语:上面的三种方式均可以实现多个任务的同时进行,对于初学者来说,个人认为找到一个适合自己使用习惯的就可以了。Institutional Review Board Statement: Not applicable.
Informed Consent Statement: Not applicable.
Data Availability Statement: Not applicable.
Author Contributions:All authors participated in the assisting performance study and approved the paper.
Conflicts of Interest: The authors declare no conflict of interest