Python中的多进程或多线程动态刷新打印

8
我已经实现了一个多进程下载器。我要如何打印状态栏(完成率、下载速度),并在终端的不同部分自动刷新?
像这样:
    499712  [6.79%]   68k/s     // keep refreshing
    122712  [16.79%]   42k/s    // different process/thread
     99712  [56.32%]   10k/s

代码:

download(...)
...
    f = open(tmp_file_path, 'wb')
    print "Downloading: %s Bytes: %s" % (self.file_name, self.file_size)
    file_size_dl = 0
    block_sz = 8192
    start_time = time.time()
    while True:
        buffer = self.opening.read(block_sz)
        if not buffer:
            break

        file_size_dl += len(buffer)
        f.write(buffer)
        end_time = time.time()
        cost_time = end_time - start_time
        if cost_time == 0:
            cost_time = 1
        status = "\r%10d  [%3.2f%%]  %3dk/s" % (file_size_dl,
                file_size_dl * 100. / self.file_size,
                file_size_dl * 100. / 1024 / 1024 / cost_time)
        print status,
        sys.stdout.flush()
    f.close()

DownloadProcess继承了Process类,并触发下载方法。

我使用队列来存储URL。这是启动进程的方式。

  ...
  for i in range(3):
    t = DownloadProcess(queue)
    t.start()
    for url in urls:
        queue.put(url)
  queue.join()

你能否提供一些代码让我们改进? - Mark Garcia
http://excess.org/urwid/ 可能会对您有所帮助,但仅限于为任务提供漂亮的可定位小部件。您需要在主进程中运行一个管理您正在显示的所有列表的“头”。不过,使用网页可能会更容易些。 - synthesizerpatel
@synthesizerpatel 我不想使用完整的功能库,我只需要一个简单的显示。 - Bob Sun
1个回答

26

下面是一个演示,它实现了多处理和多线程。要尝试其中之一,只需取消代码顶部的导入行的注释即可。如果您在单行上有进度条,则可以使用打印'\r'将光标移回到该行的开头的技术。但是,如果您想要多行进度条,那么您就需要变得更加复杂。我只是在每次想要打印进度条时清除了屏幕。查看文章console output on Unix in Python ,它在编写以下代码方面帮助了我很多。他展示了两种技术。您还可以尝试Python标准库的curses库。提出了类似问题的问题Multiline progress bars。主线程/进程产生执行工作并使用队列向主线程传递其进度的子线程。我强烈推荐使用队列进行进程/线程间通信。主线程然后显示进度,并在所有子线程执行结束之前等待自己退出。

代码

import time, random, sys, collections
from multiprocessing import Process as Task, Queue
#from threading import Thread as Task
#from Queue import Queue

def download(status, filename):
    count = random.randint(5, 30)
    for i in range(count):
        status.put([filename, (i+1.0)/count])
        time.sleep(0.1)

def print_progress(progress):
    sys.stdout.write('\033[2J\033[H') #clear screen
    for filename, percent in progress.items():
        bar = ('=' * int(percent * 20)).ljust(20)
        percent = int(percent * 100)
        sys.stdout.write("%s [%s] %s%%\n" % (filename, bar, percent))
    sys.stdout.flush()

def main():
    status = Queue()
    progress = collections.OrderedDict()
    workers = []
    for filename in ['test1.txt', 'test2.txt', 'test3.txt']:
        child = Task(target=download, args=(status, filename))
        child.start()
        workers.append(child)
        progress[filename] = 0.0
    while any(i.is_alive() for i in workers):
        time.sleep(0.1)
        while not status.empty():
            filename, percent = status.get()
            progress[filename] = percent
            print_progress(progress)
    print 'all downloads complete'

main()

演示

输入图像描述


非常出色。比我想象中的庞然大物简单得多。谢谢。 - Cerin
@Cerin 这在Python中几乎总是成立的 :) - Roger Dahl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接