Python中如何实现多线程下载时同时显示多个进度条?

希望能分享点相关的代码示例或资料

开始时想用 tqdm 来着,但总有显示串行等奇怪的 bug.

相关代码大致如下:

from tqdm import tqdm
from multiprocessing.dummy import Pool

def dw(url): r = requests.get(url, stream=True) size = int(r.headers[‘Content-Length’]) progress = tqdm(total=size) with open(‘file’, ‘wb’) as f: for chunk in r.iter_content(1024): progress.update(len(chunck)) f.write(chunck) progress.close()

pool = Pool(5) for i in urls: pool.apply_async(dw,args=(i)) pool.close() pool.join()


Python中如何实现多线程下载时同时显示多个进度条?

11 回复

如果用多线程下载一个文件,那么下载的只是同一个文件的不同区域,分开显示意义不大。


核心方案:threading 起多个下载线程,每个线程独立更新自己的进度条。tqdm 库是显示多个进度条最直接的工具,它线程安全,能自动处理并发更新。

直接上代码:

import threading
import requests
from tqdm import tqdm
import os

def download_file(url, save_path, desc):
    """
    带进度条的下载函数
    """
    response = requests.get(url, stream=True)
    total_size = int(response.headers.get('content-length', 0))

    with open(save_path, 'wb') as file, tqdm(
        desc=desc,
        total=total_size,
        unit='B',
        unit_scale=True,
        unit_divisor=1024,
        # 确保每个进度条有独立的位置,避免刷新冲突
        position=None if total_size > 0 else None, # tqdm自动处理
        leave=True  # 下载完成后保留进度条
    ) as bar:
        for data in response.iter_content(chunk_size=1024):
            size = file.write(data)
            bar.update(size)

def main():
    # 示例:同时下载三个文件
    download_list = [
        ("https://example.com/file1.zip", "file1.zip", "文件1"),
        ("https://example.com/file2.zip", "file2.zip", "文件2"),
        ("https://example.com/file3.zip", "file3.zip", "文件3")
    ]

    threads = []
    for url, save_path, desc in download_list:
        # 跳过已存在的文件(可选)
        if os.path.exists(save_path):
            print(f"{desc} 已存在,跳过")
            continue
        thread = threading.Thread(target=download_file, args=(url, save_path, desc))
        threads.append(thread)
        thread.start()

    # 等待所有线程完成
    for thread in threads:
        thread.join()

    print("所有下载完成!")

if __name__ == "__main__":
    main()

关键点:

  1. tqdm 是核心:它原生支持多进度条,position 参数可手动控制位置,但并发时建议让它自动管理(设为 None)。
  2. 线程安全tqdm 的更新操作是线程安全的,多个线程同时 update() 不会错乱。
  3. 流式下载:用 requests.get(stream=True)iter_content 分块读取数据,才能实时更新进度。
  4. 描述清晰:通过 desc 参数给每个进度条起名,方便区分。

运行效果: 你会看到三个进度条同时独立更新,类似这样:

文件1: 45%|████████████████▍              | 45.2M/100M [00:12<00:18, 3.2MB/s]
文件2: 78%|███████████████████████████▊   | 78.1M/100M [00:12<00:03, 6.1MB/s]
文件3: 23%|██████████▌                    | 23.5M/100M [00:12<00:42, 1.8MB/s]

一句话建议:tqdm 库,每个下载线程独立创建进度条实例即可。

可能我表达有误,同时下载多个文件.

不同线程创建自己的 tqdm 实例,传递不同的 position 参数。话说,你们用库都不看文档的吗?

试过了,还是有 bug.

具体什么样的 bug ?

多行的进度条大约需要 curses 来实现(win 下没有原生的 curses)

或者你可以使用别人弄好的,例如 blessings

我觉得你需要贴张图说明是什么样的 bug。另外,多个线程可以同时向终端 print,可能会造成混乱。加个锁试试。


i.imgur. com/90RV9G6

(没办法只能切开,v2ex 这条回复无法由太新的账号发出)
链接重复显示,实际上红圈里文件上就下载了一次

上面给的 issue 的链接有我写的示例代码,需要对创建 tqdm、tqdm.update 和 tqdm.close 都要加锁。另外还有个 position 的小技巧,要从 1 开始

回到顶部