使用线程/多进程读取多个文件

7

我目前正在从FileNameList的路径列表中提取.txt文件,这是有效的。但我的主要问题是,当文件太多时速度太慢。

我正在使用以下代码打印txt文件列表:

import os
import sys

#FileNameList is my set of files from my path
for filefolder in FileNameList: 
  for file in os.listdir(filefolder): 
    if "txt" in file:
        filename = filefolder + "\\" + file     
        print filename

任何关于如何使用线程/多进程并提高读取速度的帮助或建议都将被接受。提前致谢。

2
多线程/多进程无法提高速度,瓶颈在于存储设备。 - Cyphase
有没有可能使用除 .txt 以外的其他文件类型,或者提前将其转换为其他文件类型,然后在需要它们时获得加速?那么将会有巨大的加速效果。 - HeinzKurt
你是什么意思,@HeinzKurt? - Cyphase
我想知道是否可以用更好的压缩方式将 .txt 文件类型替换为另一种文件类型。 - HeinzKurt
@HeinzKurt,虽然很有趣,但只有在特定情况下才能加速程序。 - Cyphase
显示剩余2条评论
4个回答

5
你的意思是没有办法加快这个过程吗?因为我的场景是读取大量的文件,然后读取每一行并将其存储到数据库中。
优化的首要原则是问自己是否值得去做。如果你的程序只运行一次或几次,那么优化它就是浪费时间。
其次,在执行任何其他操作之前,请先度量问题所在;
编写一个简单的程序,顺序读取文件,将它们拆分成行并将其填充到数据库中。 在 profiler 下运行该程序以查看程序花费最多时间的地方。
只有这样,你才知道需要加速程序的哪个部分。
以下是一些指针。
  • 使用 mmap 可以加快读取文件的速度。
  • 您可以使用 multiprocessing.Pool 将多个文件的读取分布在不同的核心上。但是,这些文件的数据最终会进入不同的进程,并且必须通过 IPC 发送回父进程。对于大量数据来说,这会产生显著的开销。
  • 在 Python 的 CPython 实现中,每次只能有一个线程执行 Python 字节码。虽然实际的文件读取不受此限制,但处理结果受到影响。因此,线程是否提供改进是值得怀疑的。
  • 将行填充到数据库中可能始终是一个主要的瓶颈,因为这是所有内容汇聚的地方。这个问题的严重程度取决于数据库。它是否在内存或磁盘上,是否允许多个程序同时更新等。

4
多线程或多进程并不会加快速度;你的瓶颈在于存储设备。

1
你在问题中的代码并不是这样做的。即使在那种情况下,除非你对这些数据进行了大量处理,否则瓶颈将是IO;从存储设备读取和写入数据库。 - Cyphase
3
想象一下,你有10个人(线程)每分钟从一个地方(存储设备)搬运1桶水(数据)到另一个地方(数据库);如果在源头(存储设备)每分钟只产生1桶水(数据),那么不管你有多少人(线程),你每分钟只能搬运1桶水(数据);实际上,拥有更多的人(线程)会因为所有的开销而减慢速度。 - Cyphase
2
如果在从源(存储设备)获取水(数据)并将其传输到目的地(数据库)之间有一个漫长的净化(处理)步骤,那么拥有更多的人(线程)将会有所帮助。然后您可以同时拥有10个人(线程)进行净化(处理)。请注意,当我说线程时,我指的是线程或进程。特别是在Python中,由于全局解释器锁定(GIL),您需要使用进程来加速计算。 - Cyphase
@BrunoRein,我不确定你的意思。如果这是同一个问题的一部分,您应该将任何澄清编辑到问题中。如果这是另一个问题,您应该发布一个新问题。 - Cyphase
1
@BrunoRein,你修改了吗?我还没有看到。如果没有,那么再次说明,多线程/多进程不会加速获取文件列表。瓶颈在于存储设备。 - Cyphase
显示剩余7条评论

2
您可以根据文件的数量和大小来加速程序。请参见类似问题的答案:Efficient file reading in python with need to split on '\n'。您可以使用多线程、多进程或其他方式(例如迭代器)并行读取多个文件,从而可能获得一些加速。最简单的方法是使用像pathos这样的库(是的,我是作者),它在一个通用API中提供了多进程、多线程和其他选项,因此您只需编写一次代码,然后在不同的后端之间切换,直到找到最适合您情况的最快速度。有许多不同类型的映射选项(在pool对象上),您可以在此处查看:Python multiprocessing - tracking the process of pool.map operation。虽然以下示例不是最具想象力的示例,但它显示了双重嵌套映射(相当于双重嵌套循环)以及如何轻松更改其后端和其他选项。
>>> import pathos
>>> p = pathos.pools.ProcessPool()
>>> t = pathos.pools.ThreadPool()
>>> s = pathos.pools.SerialPool()
>>> 
>>> f = lambda x,y: x+y
>>> # two blocking maps, threads and processes
>>> t.map(p.map, [f]*5, [range(i,i+5) for i in range(5)], [range(i,i+5) for i in range(5)])
[[0, 2, 4, 6, 8], [2, 4, 6, 8, 10], [4, 6, 8, 10, 12], [6, 8, 10, 12, 14], [8, 10, 12, 14, 16]]
>>> # two blocking maps, threads and serial (i.e. python's map)
>>> t.map(s.map, [f]*5, [range(i,i+5) for i in range(5)], [range(i,i+5) for i in range(5)])
[[0, 2, 4, 6, 8], [2, 4, 6, 8, 10], [4, 6, 8, 10, 12], [6, 8, 10, 12, 14], [8, 10, 12, 14, 16]]
>>> # an unordered iterative and a blocking map, threads and serial
>>> t.uimap(s.map, [f]*5, [range(i,i+5) for i in range(5)], [range(i,i+5) for i in range(5)])
<multiprocess.pool.IMapUnorderedIterator object at 0x103dcaf50>
>>> list(_)
[[0, 2, 4, 6, 8], [2, 4, 6, 8, 10], [4, 6, 8, 10, 12], [6, 8, 10, 12, 14], [8, 10, 12, 14, 16]]
>>> 

我发现通常情况下,无序迭代映射(uimap)是最快的,但你必须不关心处理的顺序,因为返回时可能会出现乱序。就速度而言…在上述操作周围加上类似于 time.time 的调用即可。

获取 pathos 请访问:https://github.com/uqfoundation


1
在这种情况下,您可以尝试使用多线程。但请注意,由于Python全局解释器锁(GIL),每个非原子操作都将在单个线程中运行。如果您正在运行多台机器,则可能会更快。您可以使用类似于工人生产者的东西:
- 生产者(一个线程)将保存文件列表和队列 - 工人(多个线程)将从队列收集文件信息并将内容推送到数据库
请查看多处理中的队列和管道(真正的独立子进程)以避开GIL。
通过这两个通信对象,您可以构建一些不错的阻塞或非阻塞程序。
附注:请记住,并非所有的db连接都是线程安全的。

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