【问题标题】:how to append multiple files using multithread in bash如何在bash中使用多线程追加多个文件
【发布时间】:2019-04-25 11:43:25
【问题描述】:

我们如何使用多线程将多个文件附加到一个文件中,我的每个文件都有 10M 行。所以我想同时处理所有文件?

 #!/bin/bash
appendFiles  A.TXT &
appendFiles  B.TXT &
appendFiles  C.TXT &
wait

function appendFiles 
 {
while  read -r line; do
echo $line >>final.txt
done < $1
} 

【问题讨论】:

  • 合并文件中的记录顺序是否重要?意思是这3个文件的记录可以混淆吗?
  • 不,可以是任意顺序

标签: bash shell unix sh


【解决方案1】:

您是否尝试过像这样使用简单的cat

cat A.txt B.txt C.txt > final.txt

这比逐行读取每个文件要快得多,即使它是并行完成的。

您也可以尝试并行cat,但对于我的测试,它并不比在一个命令中执行它快。 (测试了三个文件,大约 10M 行)

#!/bin/bash
appendFiles  A.TXT &
appendFiles  B.TXT &
appendFiles  C.TXT &
wait

function appendFiles 
{
   cat "$1" >> final.txt
} 

【讨论】:

  • 最后一个解决方案有半线混合的风险。通常是个坏主意。
【解决方案2】:

我会离开 cmets,但是这有很多问题。如果这听起来很刺耳,请原谅我;这是一个很常见的误解,我想简洁明了,而不是礼貌。

作为基本术语修复,这里没有线程。有两种不同的并发模型,Bash 只支持其中一种,即多处理。线程发生在单个进程内;但是在 Bash 中没有办法管理其他进程的内部结构(无论如何,这确实会带来很大的问题)。 Bash 可以启动和停止进程(不是线程),而且做得很好。

但是为了加速不受 CPU 限制的任务而增加 CPU 并发性是一个完全有缺陷的想法。 I/O 需要时间的原因是您的磁盘很慢。在您旋转时,您的 CPU 大部分时间都处于空闲状态 从 CPU 的角度来看,磁盘(甚至 SSD)以冰冷的速度填充和清空 DMA 缓冲区。

事实上,添加更多进程来竞争有限的 I/O 容量可能会使事情变慢, 而不是更快;因为 I/O 通道将被引导尝试一次做很多事情,在这种情况下保持局部性会更好(不要在不相关的文件之间移动磁盘头,因为你必须从现在开始向后移动几毫秒;或者类似地对于 SSD,虽然关键影响要小得多,但流式传输连续的内存区域将比分散的随机访问更有效)。

除此之外,您对 cat 的错误重新实现将非常缓慢。 Bash 是notorious,因为在while read 循环中效率非常低。 (主要错误是quoting,但您也想避免corner cases with read。)

此外,您正在打开文件,寻找文件末尾以进行追加,并在每次循环中再次关闭它。您可以通过将重定向移到循环外来避免这种情况;

while IFS= read -r line || [[ -n $line ]]; do
    printf '%s\n' "$line"
done >>final.txt

但这仍然受到while read 固有的极度缓慢的影响。如果你真的想合并这些文件,我会简单地cat它们全部串行。

cat A.TXT B.TXT C.TXT >final.txt

如果 I/O 性能确实是一个问题,那么将许多文本文件组合成一个文本文件可能是朝着错误方向迈出的一步。对于需要多次阅读的信息,将其读入数据库是加快速度的常用方法。初始化和索引数据库会预先增加一些开销,但是当您可以比将它们放在顺序文件中更快、更方便地迭代字段和记录时,这很快就会得到回报。

【讨论】:

    【解决方案3】:

    通常磁盘在进行顺序读取时性能最佳。这就是为什么如果您只有一个磁盘,这通常是最好的解决方案:

    cat file1 file2 file3 > file.all
    

    但如果您的磁盘是分布式网络文件系统或 RAID 系统,那么事情的执行可能会完全不同。在这种情况下,您可以通过并行读取文件来提高性能。

    然而,最明显的解决方案是不好的:

    (cat file1 & cat file2 & cat file3 &) > file.all
    

    这是因为您可能会将来自file1 的行的前半部分与来自file2 的行的后半部分混合在一起。

    如果您改为使用parcat(GNU Parallel 的一部分),那么您将不会看到这种混合,因为它旨在防止这种情况发生:

    parcat file1 file2 file3 > file.all
    

    或(较慢,但基本相同):

    parallel --line-buffer -j0 cat ::: file1 file2 file3 > file.all
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-06
      • 2011-06-26
      • 2019-10-14
      • 2017-12-16
      • 1970-01-01
      • 1970-01-01
      • 2019-06-15
      相关资源
      最近更新 更多