【问题标题】:How to unzip multiple gz files in python using multi threading?如何使用多线程在python中解压缩多个gz文件?
【发布时间】:2015-12-24 10:51:55
【问题描述】:

我有多个 gz 文件,总大小约为 120GB。我想将这些文件解压缩(gzip)到同一目录并删除现有的 gz 文件。目前我们正在手动进行,使用gzip -d <filename>解压缩需要更多时间。
有没有一种方法可以通过创建 python 脚本或任何其他技术来并行解压缩这些文件。目前这些文件位于 Linux 机器上。

【问题讨论】:

    标签: python linux multithreading gzip


    【解决方案1】:

    您可以使用multiprocessing Pools 轻松做到这一点:

    import gzip
    import multiprocessing
    import shutil
    
    filenames = [
        'a.gz',
        'b.gz',
        'c.gz',
        ...
    ]
    
    def uncompress(path):
        with gzip.open(path, 'rb') as src, open(path.rstrip('.gz'), 'wb') as dest:
            shutil.copyfileobj(src, dest)
    
    with multiprocessing.Pool() as pool:
        for _ in pool.imap_unordered(uncompress, filenames, chunksize=1):
            pass
    

    这段代码将产生几个进程,每个进程一次提取一个文件。

    这里我选择了chunksize=1,以避免在某些文件大于平均值时导致进程停止。

    【讨论】:

    • 嗨 Andrea,感谢您的回复。所以如果我理解正确。我们一次处理 4 个文件,对吗?如果一个文件完成,那么它将选择下一个文件(第 5 个文件)。请确认。
    • 感谢您的确认,如果我不想硬编码文件而不是传递目录位置以便它会拾取文件怎么办。pool.imap_unordered 方法是否接受目录位置作为文件名的输入?
    • @user3743797:好吧,你可以为所欲为。 filenames 这里是硬编码的,但它可以是os.listdir()glob() 的结果。这取决于你。
    • 我不会将进程数传递给Pool。最佳数量取决于核心数量,Pool 默认使用正确的数量。强制 4 只有一个“正面”效果,即您知道将使用多少个进程(这可能对调试有用)和一个强烈的负面效果,即在 4 核 CPU 上它与Pool() 和任何其他 cpu 会运行得更慢。
    • @Bakuriu:对。很高兴有一个更容易分析的数字,但我同意最好避免这种情况
    【解决方案2】:

    gunzipgzip -d 解压缩文件所花费的大部分挂钟时间将来自I/O 操作(读取和写入磁盘)。它甚至可能超过实际解压缩数据所花费的时间。您可以通过在后台运行多个 gzip 作业来利用这一点。由于某些作业在 I/O 上被阻塞,另一个作业实际上可以运行而无需在队列中等待。

    您可以通过在后台运行多个gunzip 进程来加快整个文件集的解压缩。每个服务于一组特定的文件。

    您可以在 BASH 中创建一些简单的东西。将文件列表拆分为单独的命令并使用& 将其作为后台作业启动。然后wait 完成每项工作。

    我建议您同时进行 2 到 2*N 个工作。其中 N 是计算机上的内核或逻辑处理器的数量。进行适当的试验以获得正确的数字。

    您可以在 BASH 中轻松搞定。

    #!/bin/bash
    
    argarray=( "$@" )
    len=${#argarray[@]}
    
    #declare 4 empty array sets
    set1=()
    set2=()
    set3=()
    set4=()
    
    # enumerate over each argument passed to the script
    # and round robin add it to one of the above arrays
    
    i=0
    while [ $i -lt $len ]
    do
    
        if [ $i -lt $len ]; then
            set1+=( "${argarray[$i]}" )
            ((i++))
        fi
    
        if [ $i -lt $len ]; then
            set2+=( "${argarray[$i]}" )
            ((i++))
        fi
    
        if [ $i -lt $len ]; then
            set3+=( "${argarray[$i]}" )
            ((i++))
        fi
    
        if [ $i -lt $len ]; then
            set4+=( "${argarray[$i]}" )
            ((i++))
        fi
    done
    
    # for each array, start a background job
    gzip -d ${set1[@]} &
    gzip -d ${set2[@]} &
    gzip -d ${set3[@]} &
    gzip -d ${set4[@]} &
    
    # wait for all jobs to finish    
    wait
    

    在上面的示例中,我为每个作业选择了 4 个文件并开始了两个单独的作业。您可以轻松扩展脚本以拥有更多作业、每个进程更多文件,并将文件名作为命令行参数。

    【讨论】:

    • 感谢您的回复,但我想自动执行此过程。假设我的目录中有 50 个文件,那么我希望将这些文件并行解压缩,以便我可以减少时间。目前我有文件名标准序列号从 01 到 50,所以我在一个 gzip 命令中传递前 10 个文件,就像我有 5 个进程一样。所以我想为前 5 个作业创建一个线程,就像我可以启动 10 个线程一样。我的问题是在python中可能吗?
    • 通过一点 bash 脚本,你可以得到这个。将 bash 脚本的命令行参数转换为数组。然后分成4个单独的数组。每个数组都成为一个单独的gzip -d 调用。我会看看我以后能不能做点什么……
    • @user3743797 - 现在可以了。你可以调用script.sh *.gz 或者它会做你想做的事。我建议将脚本放在与您要操作的文件集不同的目录中。
    猜你喜欢
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多