在我的多进程 multiprocessing.Process有同学留言 如何获取 子进程处理的结果
。这里开一篇博客讲解。
其实这里涉及到同步
与异步
的概念。对于多线程/进程 而言,它们其实是一种异步
操作。
对于异步操作,分为不需要获取异步结果
与需要获取异步结果
,我们应该尽量设计成第一种方式。
一、不需要获取异步返回结果
对于大部分的 多线程/进程
| 异步操作
[后面统称为异步操作
],我们应尽量在异步操作
中完成所有任务,这是可以通过设计控制的。
举例几个场景:
- 在前端的数据加载过程,我们会发送
异步ajax
请求并获取数据,这个时候我们不应该让主控流程
等待异步加载的结果,获取结果后再渲染到页面上。
正确的做法是将获取
+渲染
的任务都放到异步操作中,主控流程只负责发送异步请求,然后多个异步请求各自完成自己的任务。
2)多个线程/进程
| 异步
有关联的操作。
多线程 / 进程有一个经典的入门案例:生产者与消费者模式。
按照同步的思想,应该是[在主控程序中
]通过生产者获取数据,然后调用消费者去消耗数据。
但是在多线程 |进程
中,我们应该在子线程中完成任务。所以应该这样设计:
在生产者
与 消费者
中分别传入一个容器,他们分别在容器中put
,get
数据
"""模拟生产者,消费者"""
from multiprocessing import Process, Queue
import time, random
def create(queue: Queue):
"""具有生产功能的函数
向 queue 中添加 url 网址
"""
for value in range(100):
# 随机停顿 0 - 1秒,模拟生产者的生产动作
time.sleep(random.random())
value = 'http://www.baidu.com/' + str(value) + '/'
queue.put(value)
def consume(queue: Queue):
"""具有消费功能的函数
从 queue 中消耗数据 url 数据
"""
while True:
value = queue.get()
print('向目标 url 爬取数据:' + value)
if __name__ == '__main__':
# 主控流程
url_pool = Queue(maxsize=10)
creater = Process(target=create, args=(url_pool,))
consumer = Process(target=consume, args=(url_pool,))
creater.start()
consumer.start()
小结,对于大部分的IO
操作,使用多线程/进程
可以大大的减少程序执行时间,在设计多线程/进程
时,我们应尽量将任务在子线程/进程
中完成。
作为一个成熟的子线程/进程,你应该能够独立完成工作。包含数据处理,存储,异常等待。
一个子线程处理的任务量可大可小。我们可以通过一个子线程发送邮件,对服务器而言,可以通过一个子线程去处理用户请求,我们后端的所以操作都是在这个子线程中完成。
二、需要获取异步结果
虽然上面讲了很多的不需要获取异步结果的案例。当我们也有很多必须要获取异步结果的场景。
比如我们将一个很大的计算分成多个小计算,然后对这些小计算的结果汇总。
比如做大文件上传,将大文件拆分成小文件,并获取上传成功的小文件标示 ...
对于需要获取异步结果场景,python
有单独的多线程/进程执行器
,分别是
ProcessPoolExecutor
,ThreadPoolExecutor
from concurrent.futures import ProcessPoolExecutor
import time
def long_compute_sum(v1: int, v2: int):
"""需要计算很长[3秒]的加法"""
time.sleep(3)
return v1 + v2
if __name__ == '__main__':
executor = ProcessPoolExecutor(max_workers=4)
future = executor.submit(long_compute_sum, 100, 200) # 获取期程对象
print(future.result()) # 从期程对象中获取返回结果
网友评论