显示我的多线程进程的进度条

时间:2014-03-10 10:10:48

标签: python multithreading flask

我有一个简单的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上看过类似的问题,但是我无法根据我的情况调整答案。

谢谢。

3 个答案:

答案 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.Valuemultiprocessing.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之前获得锁定,然后释放锁定