我有一个简单的Flask Web应用程序,当用户按下按钮时,它会向外部服务发出许多HTTP请求。在客户端,我有一个angularjs应用程序。
代码的服务器端看起来像这样(使用multiprocessing.dummy):
worker = MyWorkerClass()
pool = Pool(processes=10)
result_objs = [pool.apply_async(worker.do_work, (q,))
for q in queries]
pool.close() # Close pool
pool.join() # Wait for all task to finish
errors = not all(obj.successful() for obj in result_objs)
# extract result only from successful task
items = [obj.get() for obj in result_objs if obj.successful()]
正如您所看到的,我正在使用apply_async
因为我想稍后检查每个任务,并且只有在任务没有引发任何异常时才从中提取结果。
我明白为了在客户端显示进度条,我需要在某处发布已完成任务的数量,所以我做了一个简单的视图:
@app.route('/api/v1.0/progress', methods=['GET'])
def view_progress():
return jsonify(dict(progress=session['progress']))
这将显示会话变量的内容。现在,在此过程中,我需要使用已完成任务的数量更新该变量(要完成的任务总数是固定且已知的)。
关于如何做到这一点的任何想法?我正朝着正确的方向努力?
我在this one上看过类似的问题,但是我无法根据我的情况调整答案。
谢谢。
答案 0 :(得分:7)
对于进程间通信,您可以使用multiprocessiong.Queue,并且您的工作人员可以在进行工作时使用put_nowait
元组及其进度信息。您的主进程可以更新您的view_progress正在读取的内容,直到所有结果都准备就绪。
有点像这个example usage of a Queue,有一些调整:
在作家(工作人员)中,我使用put_nowait
而不是put
,因为工作比等待报告您正在工作更重要(但也许您判断其他情况并决定通知用户是任务的一部分,永远不应该被跳过)。
示例只是队列中的puts
个字符串,我使用collections.namedtuples来获取更多结构化消息。在包含许多步骤的任务中,您可以提高进度报告的分辨率,并向用户报告更多信息。
答案 1 :(得分:2)
一般来说,你采取的方法是可以的,我也是这样做的。
要计算进度,您可以使用辅助功能来计算已完成的任务:
def get_progress(result_objs):
done = 0
errors = 0
for r in result_objs:
if r.ready():
done += 1
if not r.successful():
errors += 1
return (done, errors)
请注意,作为奖励,此功能会返回已完成的"完成的数量。任务以错误结束。
最大的问题是/api/v1.0/progress
路由找到AsyncResult
个对象的数组。
不幸的是,AsyncResult对象无法序列化为会话,因此该选项已经完成。如果您的应用程序一次只支持一组异步任务,那么您可以将此数组存储为全局变量。如果您需要支持多个客户端,每个客户端都有一组不同的异步任务,那么您需要找出一种策略来将客户端会话数据保存在服务器中。
我将单客户端解决方案实现为快速测试。我的视图功能如下:
results = None
@app.route('/')
def index():
global results
results = [pool.apply_async(do_work) for n in range(20)]
return render_template('index.html')
@app.route('/api/v1.0/progress')
def progress():
global results
total = len(results)
done, errored = get_progress(results)
return jsonify({'total': total, 'done': done, 'errored': errored})
我希望这有帮助!
答案 2 :(得分:0)
我认为您应该能够使用multiprocessing.Value和multiprocessing.Lock更新已完成任务的数量。
在您的主要代码中,使用:
processes=multiprocessing.Value('i', 10)
lock=multiprocessing.Lock()
然后,当你调用worker.dowork时,将一个锁对象和值传递给它:
worker.dowork(lock, processes)
在您的worker.dowork代码中,减少"进程"在代码完成时用一个:
lock.acquire()
processes.value-=1
lock.release()
现在," processes.value"应该可以从主代码访问,并且等于剩余进程的数量。确保在访问processes.value之前获得锁定,然后释放锁定