这一篇教程,我们一起来了解Python的多进程。
首先,需要知道进程的概念。
它的概念有些枯燥。
狭义的定义:进程是正在运行的程序的实例。
广义的定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是操作系统动态执行的基本单元。
是不是看着头晕了?
那就忘掉它。
你需要知道的是,进程只会在程序运行时出现。
程序的概念是指令和数据的有序集合,但是其本身没有任何运行的含义,是一个静态的概念。
而进程是程序的一次执行过程,它是一个动态的概念。
以windows系统中为例,你下载了一个软件程序,在你没有运行它之前,它只是一个静态的程序文件。
当我们打开这个软件程序,这时在系统任务管理器的进程列表中,我们能够看到这个软件程序的进程。
当我们关闭这个软件程序,这时在系统任务管理器的进程列表中,和这个软件程序对应的进程会消失。
再次打开软件程序,进程又会再次出现。
当我们在系统任务管理器中结束这个软件程序的进程,这个软件程序也会被结束运行。
这个时候,你是否能够理解进程的狭义定义了呢?
死记硬背概念是没有用的,理解才是最重要的。
我们来看一段代码!
示例代码:
import time def task01(name): print(name, 1) time.sleep(0.001) print(name, 2) time.sleep(0.001) print(name, 3) def task02(name): print(name, 1) time.sleep(0.001) print(name, 2) time.sleep(0.001) print(name, 3) if __name__ == '__main__': task01('函数1:') task02('函数2:')
在上方代码中,我定义了两个函数。
然后,在运行代码时调用了这两个函数。
程序运行的结果为:
函数1: 1
函数1: 2
函数1: 3
函数2: 1
函数2: 2
函数2: 3
不管运行多少次,都是这样的结果。
通过这个结果我们能够看出,两个函数是按顺序,先执行完上方的task01,再执行下方的task02。
而当我们执行这段代码时,实际上我们已经开启了一个进程。
正是这个进程完成了这段代码的执行。
我们继刚才的代码之后,再加入一些代码看一下。
示例代码:
import multiprocessing print(multiprocessing.current_process().name) # 显示输出结果为:MainProcess
multiprocessing模块为多进程模块,可以通过这个模块中的current_process()函数,访问当前进程实例的name特性,这个特性是当前进程实例的名称。
通过运行上方代码,我们看到了一个进程名称:MainProcess(主进程)
任何程序在运行时,都至少有一个进程,也就是说都会有MainProcess这个主进程的存在。
既然提到主进程,那么,显然还有不是主进程的进程,也就是子进程。
多进程模块,能够让我们在主进程的基础之上,添加多个子进程。
例如刚才的的代码中,运行程序时只能按照次序运行。
如果,我们想在程序运行时,两个函数一起执行是否可以呢?
用多进程,每个进程执行不同的函数就可以实现这样的效果。
我们在之前的代码上增加一些新内容。
示例代码:
import time,multiprocessing # 导入需要使用的模块 def task01(name): print(multiprocessing.current_process().name) # 显示输出结果为:Process-1 print(name, 1) time.sleep(0.001) print(name, 2) time.sleep(0.001) print(name, 3) def task02(name): print(multiprocessing.current_process().name) # 显示输出结果为:Process-2 print(name, 1) time.sleep(0.001) print(name, 2) time.sleep(0.001) print(name, 3) if __name__ == '__main__': print(multiprocessing.current_process().name) # 显示输出结果为:MainProcess process1 = multiprocessing.Process(target=task01, args=('函数01:',)) # 创建子进程运行函数task01 process2 = multiprocessing.Process(target=task02, args=('函数02:',)) # 创建子进程运行函数task02 process1.start() # 启动子进程 process2.start() # 启动子进程
在之前的代码中,新增了一些代码(带有注释的部分)。
通过这些代码,创建了子进程,并在主进程运行时,启动这些子进程。
当我们运行程序,显示输出结果为:
MainProcess
Process-1
函数01: 1
函数01: 2
函数01: 3
Process-2
函数02: 1
函数02: 2
函数02: 3
这个运行结果看上去,仍然是先执行了函数task01(进程process1),再执行了函数task02(进程process2)。
先别管这个结果,我们把每个time.sleep()的语句中的参数都从“0.001”改成“0.01”,再运行程序看一下。
提示:在PyCharm中可以通过导航菜单-编辑(Edit)-查找(Find)-替换(Replace)选项进行批量替换,快捷键为<Ctrl+R>。
运行程序之后,显示输出结果类似:
MainProcess
Process-1
函数01: 1
函数01: 2
Process-2
函数02: 1
函数01: 3
函数02: 2
函数02: 3
注意,每次运行的显示输出结果不一定相同,次序会发生变化。
通过上方的程序运行结果,大家能够看出函数task01和函数task02的运行过程混合在了一起。
也就是说,两个函数(进程)同时在运行(并行)。
不过,因为每次都是进程process1先启动,所以运行结果中也是函数task01先执行。
在我们运行程序时,是由cpu在顺序执行代码。
那么,也就意味着多个进程中的程序代码需要多个cpu去执行。
所以,只有多核cpu才能真正的并行执行多个进程。
但是,cpu的核心数量即便是多个,也是有限的。例如4核和8核。
当进程数量超过cpu核心数量的时候,会怎么样呢?
操作系统会让cpu执行某个进程一段时间后,就去执行另外一个进程一段是时间,如此轮流执行进程,而不是完全执行某个进程的代码后再执行下一个进程中的代码。
所以,即使单核cpu,操作系统也能让它处理多进程,这样的操作系统就是通常所说的多任务操作系统。
对于操作系统来说,一个任务就是一个进程,并非执行一个完整的程序才是一个进程。
就像前面的代码中,我们可以在同一个程序的代码中,启动多个进程去执行多个任务。
所以,一个程序可以启动多个进程,同时让多种功能同时执行。
很多软件程序也是这么做的,例如有道词典取词功能和词典功能都是独立的进程在起作用。
那么,假如取词之后要通过词典翻译,就需要两个进程之间的通信。
进程间通信,我们可以使用Queue类来实现。
通过Queue实例对象的put()和get()方法,能够送出和获取进程的数据。
例如,我们虚拟一个取词和词典进程间的通信。
示例代码:
import multiprocessing import time def ocr(que): for value in ['one', 'two', 'three']: print('完成取词...') que.put(value) # 将数据送出到进程共享队列 time.sleep(1) def dict(que): dict = {'one': '一', 'two': '二', 'three': '三'} while True: value = que.get() # 从进程共享队列获取到数据 print(value, ':', dict[value], sep='') if __name__ == '__main__': que = multiprocessing.Queue() # 创建进程共享队列 process1 = multiprocessing.Process(target=ocr, args=(que,)) process2 = multiprocessing.Process(target=dict, args=(que,)) process1.start() process2.start()
运行代码,我们可以看到如下结果:
完成取词…
one:一
完成取词…
two:二
完成取词…
three:三
看似没有问题,但是,大家注意程序没有结束。
因为,dict函数里面是死循环。
所以,我们需要结束这个进程。
继续添加一句代码:
process2.terminate() # 终止进程
运行程序,问题随之而来。显示输出结果如下:
完成取词…
完成取词…
完成取词…
这是因为进程process2还没来得及对process1进程送出的数据进行处理,就被终止了。
那么,能不能等process1的所有数据都送出,再结束进程process2呢?
在终止process2进程的代码之前,我们再加入一句代码。
process1.join() # 等待进程process1结束
join()方法能够阻塞主进程,等待调用该方法的子进程执行结束,再释放主进程的阻塞。
所以,当加入这一句代码,就能够等待process1进程执行结束后,再执行终止process2进程的代码。
最后,在Python中开启多进程,我们还可以使用进程池(Pool)。
例如,开启多个进程,分别运行不同的函数。
示例代码:
import multiprocessing, time def task01(name): print(name) time.sleep(0.01) print(name) def task02(name): print(name) time.sleep(0.01) print(name) def task03(name): print(name) time.sleep(0.01) print(name) if __name__ == '__main__': task_lst = [task01, task02, task03] # 创建需运行函数的列表。 processes = multiprocessing.Pool(3) # 创建进程池对象,设定并行最大进程数为3个。 for i in range(len(task_lst)): processes.apply_async(task_lst[i], args=('进程' + str(i + 1),)) # 维持执行的进程总数为3个,当一个进程执行完毕后会添加新的进程进去。 processes.close() # 关闭进程池对象,禁止再加入新的进程。 processes.join() # 等待所有子进程结束。
注意:进程池必须先关闭,再等待所有子进程结束。否则,会发生错误。因为,关闭了进程池,不再有新的进程加入,才能知道需要等待结束的子进程共有哪些。
本节知识点:
1、进程的概念;
2、多进程的使用。
本节英文单词与中文释义:
1、task:任务
2、process:进程
3、queue:队列
4、terminate:终止
5、join:加入
6、pool:水池
7、apply:应用
8、async:异步
转载请注明:魔力Python » Python3萌新入门笔记(47)