1. 进程
windows下不支持os.fork() 方法来启动多个进程
Python 在 multiprocessing 模块下提供了 Process 来创建新进程。与线程类似的是,使用 Process 创建新进程也有两种方式:
- 以指定函数作为 target,创建 Process 对象即可创建新进程。常用
- 继承 Process 类,并重写它的 run() 方法来创建进程类,程序创建 Process 子类的实例作为进程。(与线程继承Thread类相似,不常用)
Process 类方法和属性:
- run():重写该方法可实现进程的执行体。
- start():该方法用于启动进程。
- join([timeout]):该方法类似于线程的 join() 方法,当前进程必须等待被 join 的进程执行完成才能向下执行。
- name:该属性用于设置或访问进程的名字。
- is_alive():判断进程是否还活着。
- daemon:该属性用于判断或设置进程的后台状态。
- pid:返回进程的 ID。
- authkey:返回进程的授权 key。
- terminate():中断该进程。
# -*- coding: utf-8 -*-
from multiprocessing import Process
import os
# 子进程要执行的代码
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
# multiprocessing模块提供了一个Process类来代表一个进程对象
# 通过 multiprocessing.Process 来创建并启动进程时,程序必须先判断if __name__=='__main__':,否则可能引发异常。
if __name__ == '__main__':
print('Parent process %s.' % os.getpid())
for i in range(5):
p = Process(target=run_proc, args=(i,))
print('Child process will start.')
p.start() # 启动进程
p.join() # 等待子进程执行结束后,再继续向下运行。
print('Child process end')
2. 进程池
进程池与线程池一样实现了上下文管理协议,因此程序可以使用 with 子句来管理进程池,可以避免程序主动关闭进程池。
通过multiprocessing 模块的 Pool() 函数创建进程池
进程池具有如下常用方法:
- apply(func[, args[, kwds]]):将 func 函数提交给进程池处理。其中 args 代表传给 func 的位置参数,kwds 代表传给 func 的关键字参数。该方法会被阻塞直到 func 函数执行完成。
- apply_async(func[, args[, kwds[, callback[, error_callback]]]]):这是 apply() 方法的异步版本,该方法不会被阻塞。其中 callback 指定 func 函数完成后的回调函数,error_callback 指定 func 函数出错后的回调函数。
- map(func, iterable[, chunksize]):类似于 Python 的 map() 全局函数,只不过此处使用新进程对 iterable 的每一个元素执行 func 函数。
Python中线程池与进程池的map()方法使用方式相同
- map_async(func, iterable[, chunksize[, callback[, error_callback]]]):这是 map() 方法的异步版本,该方法不会被阻塞。其中 callback 指定 func 函数完成后的回调函数,error_callback 指定 func 函数出错后的回调函数。
- imap(func, iterable[, chunksize]):这是 map() 方法的延迟版本。
- imap_unordered(func, iterable[, chunksize]):功能类似于 imap() 方法,但该方法不能保证所生成的结果(包含多个元素)与原 iterable 中的元素顺序一致。
- starmap(func, iterable[,chunksize]):功能类似于 map() 方法,但该方法要求 iterable 的元素也是 iterable 对象,程序会将每一个元素解包之后作为 func 函数的参数。
- close():关闭进程池。在调用该方法之后,该进程池不能再接收新任务,它会把当前进程池中的所有任务执行完成后再关闭自己。
- terminate():立即中止进程池。
- join():等待所有进程完成。
# -*- coding: utf-8 -*-
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
def func(num):
res = num * num
print(res)
if __name__ == '__main__':
print('Parent process %s.' % os.getpid())
p = Pool(4)
l = range(1, 5)
# 进程池的 map() 方法会为 iterables 的每个元素启动一个进程,以并发方式来执行 func 函数
p.map(func, l)
# apply_async() 将 任务 提交给进程池,由进程池负责启动
# for i in range(8):
# p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
3. 进程间通信
Python 为进程通信提供了两种机制:
- Queue:一个进程向 Queue 中放入数据,另一个进程从 Queue 中读取数据。
- Pipe:Pipe 代表连接两个进程的管道。程序在调用 Pipe() 函数时会产生两个连接端,分别交给通信的两个进程,接下来进程既可从该连接端读取数据,也可向该连接端写入数据。
multiprocessing 模块下的 Queue 和 queue 模块下的 Queue 基本类似,它们都提供了 qsize()、empty()、full()、put()、put_nowait()、get()、get_nowait() 等方法。区别只是 multiprocessing 模块下的 Queue 为进程提供服务,而 queue 模块下的 Queue 为线程提供服务。
# -*- coding: utf-8 -*-
from multiprocessing import Process, Queue
import os, time, random
'''
在Quene 已空的情况下,程序使用 get() 方法尝试取出元素将会阻塞线程,在队列已满的情况下继续放入元素,也会阻塞线程
队列 FIFO 先进先出
LIFO 后进先出
'''
# 写数据进程执行的代码:
def write(q):
print('Process to write: %s' % os.getpid())
for value in ['A', 'B', 'C']:
print('Put %s to queue...' % value)
q.put(value)
time.sleep(random.random())
# 读数据进程执行的代码:
def read(q):
print('Process to read: %s' % os.getpid())
while True:
value = q.get(True)
print('Get %s from queue.' % value)
if __name__ == '__main__':
# 进程间通信通过 quene pipes等实现
# 父进程创建Queue,并传给各个子进程:
q = Queue()
# Process 以指定函数作为target创建新进程
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 启动子进程pw,写入:
pw.start()
# 启动子进程pr,读取:
pr.start()
# 等待pw结束:
pw.join()
# pr进程里是死循环,无法等待其结束,只能强行终止:
pr.terminate()