进程、线程和协程,在概念上虽然有差异,但都是为了解决异步编程问题,主要是效率问题。
1、概念
进程是指计算机中程序运行的一个实例,最直观的观察就是打开windows的任务管理器。
2、在Python中使用多进程
首先定义一个基本任务,示例代码如下:
def get_html(url):
# 根据提供的url下载网页
return html
(1)多进程
# 创建两个任务(进程)
p1 = multiprocessing.Process(target=get_html, args=('aa.com',))
p2 = multiprocessing.Process(target=get_html, args=('bb.com',))
# 开始任务
p1.start()
p2.start()
# 主进程等待任务1完成
p1.join()
# 主进程等待任务2两秒
p2.join(2)
print('主进程结束,如果任务2还没完成,任务2的进程会继续存在')
(2)进程池(pool.close()很关键)
# 开启进程数等于cpu数量
pool = multiprocessing.Pool(multiprocessing.cpu_count())
# 往池子里添加任务
res = pool.apply_async(get_html, args=('aa.com',))
res2 = pool.apply_async(get_html, args=('bb.com',))
# 表示不再接收任务
pool.close()
pool.join()
print(res, res2)
上述书写方式很麻烦,可以使用下面方法。这里补充一点,pool.apply_async()是非阻塞式,pool.apply()是阻塞式。简单说就是进程1完成任务1后,进程2开始做任务2。但这样设计的目的是什么,根本没发挥并行效果啊。希望大佬指点。
for url in urls:
pool.apply_async(get_html, args=(url,))
# 会按照参数传递顺序打印结果
for res in pool.imap(get_html, ['aa.com', 'bb.com']):
print(res)
# 不按参数传递顺序,先获取先打印
for res in pool.imap_unordered(get_html, ['aa.com', 'bb.com']):
print(res)
(3)进程间通信
进程是相对独立的一个对象,一个进程拥有自己的内存和数据,所以进程间并不能直接通信。要想实现进程间通信,可以单独开辟一个内存空间,让需要通信的进程都去访问这个内存。具体的实现方式有multiprocessing封装的队列(Queue)、管道(pipe)等。
1) multiprocessing.Queue 用于多进程通信。
# 定义一个全局的队列, 限定最大空间
q = multiprocessing.Queue(maxsize=10)
# 典型的一个场景-生产-消费模式
def producer():
global q
q.put('这里产生数据')
def consumer():
global q
# 这里是弹出模式
dt = q.get(timeout=2)
print(dt)
2)multiprocessing.Manager.Queue 用于进程池通信,使用方法同上。
3)multiprocessing.Pipe 用于两个进程间通信
# 注意这里实例化方式
send_p, recv_p = multiprocessing.Pipe()
send_p.send('生产数据')
dt = recv_p.recv()
print(dt)
4)multiprocessing.Manager()下的list、dict等,使用方法和python基本类型里的list、dict相同。使用这些对象可以实现像线程之间通过共享全局变量来通信一样方便。(可以对比本系列多线程篇)
p_list = Manager().list()
# 使用方法和list一致
p_list.append(1)
p_list.pop()