python中的线程-同时处理多个大文件

python中的线程-同时处理多个大文件,python,multithreading,concurrency,Python,Multithreading,Concurrency,我是python新手,在理解线程如何工作时遇到了困难。通过浏览文档,我的理解是,在线程上调用join()是推荐的阻塞方法,直到它完成为止 为了给大家一点背景知识,我有48个大的csv文件(多GB),我正试图对其进行解析,以找出不一致之处。线程不共享任何状态。这可以在一段合理的时间内一次性完成,但我正在尝试同时做这项练习 以下是文件处理的框架: def process_file(data_file): with open(data_file) as f: print "Start pr

我是python新手,在理解线程如何工作时遇到了困难。通过浏览文档,我的理解是,在线程上调用
join()
是推荐的阻塞方法,直到它完成为止

为了给大家一点背景知识,我有48个大的csv文件(多GB),我正试图对其进行解析,以找出不一致之处。线程不共享任何状态。这可以在一段合理的时间内一次性完成,但我正在尝试同时做这项练习

以下是文件处理的框架:

def process_file(data_file):
  with open(data_file) as f:
    print "Start processing {0}".format(data_file)
    line = f.readline()
    while line:
      # logic omitted for brevity; can post if required
      # pretty certain it works as expected, single 'thread' works fine
      line = f.readline()

  print "Finished processing file {0} with {1} errors".format(data_file, error_count)

def process_file_callable(data_file):
  try:
    process_file(data_file)
  except:
    print >> sys.stderr, "Error processing file {0}".format(data_file)
和并发位:

def partition_list(l, n):
    """ Yield successive n-sized partitions from a list.
    """
    for i in xrange(0, len(l), n):
        yield l[i:i+n]

partitions = list(partition_list(data_files, 4))
for partition in partitions:
  threads = []
  for data_file in partition:
    print "Processing file {0}".format(data_file)
    t = Thread(name=data_file, target=process_file_callable, args = (data_file,))
    threads.append(t)
    t.start()

  for t in threads:
    print "Joining {0}".format(t.getName())
    t.join(5)

  print "Joined the first chunk of {0}".format(map(lambda t: t.getName(), threads))
我的运行方式如下:

python -u datautils/cleaner.py > cleaner.out 2> cleaner.err
我的理解是join()应该阻止调用线程等待它被调用的线程完成,但是我观察到的行为与我的预期不一致

我从未在错误文件中看到错误,但也从未在标准输出上看到预期的日志消息

父进程不会终止,除非我从shell显式终止它。如果我检查一下我有多少张打印完成的照片…这绝不是预期的48张,而是12到15张。但是,在以单线程方式运行之后,我可以确认多线程运行实际上正在处理所有内容并执行所有预期的验证,只是它似乎没有干净地终止


我知道我一定是做错了什么,但如果您能给我指出正确的方向,我将不胜感激。

我不明白您的代码中哪里有错误。但是我可以建议你重构一下。 首先,python中的线程根本不是并发的。这只是一种错觉,因为存在一个线程,所以同一时间只能执行一个线程。这就是为什么我建议您使用:

第二,您正在使用非Python方式读取文件:

with open(...) as f:
   line = f.readline()
    while line:
       ... # do(line)
      line = f.readline()
以下是pythonic方法:

with open(...) as f:
    for line in f:
         ... # do(line)
这是内存效率高,速度快,并导致简单的代码。(c) 皮多克

顺便说一句,我只有一个假设,你的程序在多线程方式下会发生什么——应用程序变得更慢了,因为无序访问硬盘驱动器的速度明显比有序访问慢。如果您使用的是Linux,您可以尝试使用
iostat
htop
来检查这一假设


如果您的应用程序没有完成工作,并且在process monitor中没有任何功能(cpu或磁盘未处于活动状态),这意味着您遇到了某种死锁或阻止了对同一资源的访问。

感谢大家的意见,很抱歉没有及时回复-我正在断断续续地做这个爱好项目

我写了一篇文章证明这是我的错:

from itertools import groupby
from threading import Thread
from random import randint
from time import sleep

for key, partition in groupby(range(1, 50), lambda k: k//10):
  threads = []
  for idx in list(partition):
    thread_name = 'thread-%d' % idx
    t = Thread(name=thread_name, target=sleep, args=(randint(1, 5),))
    threads.append(t)
    print 'Starting %s' % t.getName()
    t.start()

  for t in threads:
    print 'Joining %s' % t.getName()
    t.join()

  print 'Joined the first group of %s' % map(lambda t: t.getName(), threads)
它最初失败的原因是while循环“为简洁起见省略了逻辑”工作正常,但是正在输入的一些输入文件已损坏(有混乱的行),并且逻辑进入了无限循环。这就是某些线程从未连接的原因。连接超时确保了它们都已启动,但有些从未完成,因此“启动”和“连接”之间不一致。另一个有趣的事实是,腐败发生在最后一行,因此所有预期的数据都在处理中


再次感谢您的建议-有关以
而不是pythonic方式处理文件的评论为我指明了正确的方向,是的,线程的行为符合预期。

可能相关:chunks
做什么?您是否在
数据文件块中多次使用同一个文件?另外,
chunks
表示您不希望一次读取整个文件,但
process\u file
似乎认为它确实读取了整个文件。最后,但不相关,您不应该直接调用
readline
。对一次只能读取一行的文件使用内置的迭代支持。感谢您的建议!关于“分块”,我的命名不好,实际上我正在对输入列表进行分区,以避免一次启动48个线程。我更新了代码示例以反映这一点。我注意到,当一次启动48个线程时,它有时会挂起
pthread\u cond\u wait:Resource busy
,并且行为变得古怪。至少在这种形式下,它是确定的,只是不是我期望看到的。顺便说一下,这也与您的问题无关,但是您的
分区列表
函数本质上是内置函数的副本。
from itertools import groupby
from threading import Thread
from random import randint
from time import sleep

for key, partition in groupby(range(1, 50), lambda k: k//10):
  threads = []
  for idx in list(partition):
    thread_name = 'thread-%d' % idx
    t = Thread(name=thread_name, target=sleep, args=(randint(1, 5),))
    threads.append(t)
    print 'Starting %s' % t.getName()
    t.start()

  for t in threads:
    print 'Joining %s' % t.getName()
    t.join()

  print 'Joined the first group of %s' % map(lambda t: t.getName(), threads)