循环中的python线程
我有一个项目,需要一堆大矩阵,它们存储在约200 MB的文件中,相互交叉相关(即FFT*conj(FFT))。文件的数量是如此之多,以至于我不能把它们全部加载,然后再进行处理。另一方面,根据需要读取每个文件的速度比我希望的慢 到目前为止,我得到的是:循环中的python线程,python,multithreading,loops,Python,Multithreading,Loops,我有一个项目,需要一堆大矩阵,它们存储在约200 MB的文件中,相互交叉相关(即FFT*conj(FFT))。文件的数量是如此之多,以至于我不能把它们全部加载,然后再进行处理。另一方面,根据需要读取每个文件的速度比我希望的慢 到目前为止,我得到的是: result=0 for i in xrange(N_files): f1 = file_reader(file_list[i]) #################################################
result=0
for i in xrange(N_files):
f1 = file_reader(file_list[i])
############################################################################
# here I want to have file_reader go start reading the next file I'll need #
############################################################################
in_place_processing(f1)
for j in xrange(i+1,N_files):
f2 = file_reader(file_list[j])
##################################################################
# here I want to have file_reader go start reading the next file #
##################################################################
in_place_processing(f2)
result += processing_function(f1,f2)
所以基本上,我只想有两个线程,每个线程读取一个文件,在我请求时给我(或者在我请求后尽快完成),然后在我请求时开始读取下一个文件。文件读取器返回的对象非常大和复杂,因此我不确定这里是否要进行多重处理
我读过关于线程和队列的内容,但似乎不知道我让线程去读文件并在程序运行时继续执行程序的部分。我不希望线程只是在后台处理它们的事务——我是在这里遗漏了一个细节,还是线程不是一条出路?下面是一个使用
多处理
模块的示例,该模块将派生子进程来调用文件读取器
方法并将其结果排队。队列满时应该阻塞,因此您可以使用queue\u SIZE
常量控制要执行的预读的数量
这利用了多进程通信的标准生产者/消费者模型,子进程充当生产者,主线程充当消费者。类析构函数中的join
方法调用确保正确清理子进程资源。为了演示的目的,还穿插了一些打印语句
此外,我还为QueuedFileReader类添加了将工作卸载到工作线程或在主线程中运行的功能,而不是使用子进程进行比较。这是通过在类初始化时分别将mode参数指定为mode\u THREADS
或mode\u SYNCHRONOUS
import multiprocessing as mp
import Queue
import threading
import time
QUEUE_SIZE = 2 #buffer size of queue
## Placeholder for your functions and variables
N_files = 10
file_list = ['file %d' % i for i in range(N_files)]
def file_reader(filename):
time.sleep(.1)
result = (filename,'processed')
return result
def in_place_processing(f):
time.sleep(.2)
def processing_function(f1,f2):
print f1, f2
return id(f1) & id(f2)
MODE_SYNCHRONOUS = 0 #file_reader called in main thread synchronously
MODE_THREADS = 1 #file_reader executed in worker thread
MODE_PROCESS = 2 #file_reader executed in child_process
##################################################
## Class to encapsulate multiprocessing objects.
class QueuedFileReader():
def __init__(self, idlist, mode=MODE_PROCESS):
self.mode = mode
self.idlist = idlist
if mode == MODE_PROCESS:
self.queue = mp.Queue(QUEUE_SIZE)
self.process = mp.Process(target=QueuedFileReader.worker,
args=(self.queue,idlist))
self.process.start()
elif mode == MODE_THREADS:
self.queue = Queue.Queue(QUEUE_SIZE)
self.thread = threading.Thread(target=QueuedFileReader.worker,
args=(self.queue,idlist))
self.thread.start()
@staticmethod
def worker(queue, idlist):
for i in idlist:
queue.put((i, file_reader(file_list[i])))
print id(queue), 'queued', file_list[i]
queue.put('done')
def __iter__(self):
if self.mode == MODE_SYNCHRONOUS:
self.index = 0
return self
def next(self):
if self.mode == MODE_SYNCHRONOUS:
if self.index == len(self.idlist): raise StopIteration
q = (self.idlist[self.index],
file_reader(file_list[self.idlist[self.index]]))
self.index += 1
else:
q = self.queue.get()
if q == 'done': raise StopIteration
return q
def __del__(self):
if self.mode == MODE_PROCESS:
self.process.join()
elif self.mode == MODE_THREADS:
self.thread.join()
#mode = MODE_PROCESS
mode = MODE_THREADS
#mode = MODE_SYNCHRONOUS
result = 0
for i, f1 in QueuedFileReader(range(N_files),mode):
in_place_processing(f1)
for j, f2 in QueuedFileReader(range(i+1,N_files),mode):
in_place_processing(f2)
result += processing_function(f1,f2)
如果中间值太大,无法通过队列,则可以在其自己的进程中执行外部循环的每个迭代。一种简便的方法是使用多处理
中的池
类,如下例所示
import multiprocessing as mp
import time
## Placeholder for your functions and variables
N_files = 10
file_list = ['file %d' % i for i in range(N_files)]
def file_reader(filename):
time.sleep(.1)
result = (filename,'processed')
return result
def in_place_processing(f):
time.sleep(.2)
def processing_function(f1,f2):
print f1, f2
return id(f1) & id(f2)
def file_task(file_index):
print file_index
f1 = file_reader(file_list[file_index])
in_place_processing(f1)
task_result = 0
for j in range(file_index+1, N_files):
f2 = file_reader(file_list[j])
in_place_processing(f2)
task_result += processing_function(f1,f2)
return task_result
pool = mp.Pool(processes=None) #processes default to mp.cpu_count()
result = 0
for file_result in pool.map(file_task, range(N_files)):
result += file_result
print 'result', result
#or simply
#result = sum(pool.map(file_task, range(N_files)))
如果要在Windows下运行,请注意真正的多处理并不存在。派生的进程实际上是作为线程创建的。由于Python的全局线程锁,线程不能同时运行。因此,您的程序实际运行速度可能比您在一个线程中完成所有读取和处理的速度要慢。这取决于IO还是处理是瓶颈。如果你是在Linux下运行的,如果你的CPU有多个内核,这就不适用了。太棒了,谢谢你——我不知道如何正确地使用队列来处理这个问题。您使用多处理有什么特别的原因吗?使用多线程来完成所有这一切会有不同吗?我写上面的代码是为了自学Python中的多处理模块,因为我以前从未使用过它。阅读文档后,一些Python操作系统实现可能会在进程之间传递大于32MB的序列化数据,考虑到您正在处理的文件的大小,这可能是一个问题。因此,使用线程而不是进程的解决方案可能更好。然而,正如Sizzlerz在上面的评论中指出的那样,GIL开始发挥作用。我将修改上面的示例,以便QueuedFileReader类可以使用线程或进程,以便您可以对其进行测试。是的,看起来GIL可能会在线程版本中发挥作用,因为将读入文件添加到队列的工作进程似乎会将处理延迟到完成为止(即:没有胜利,而只是有条不紊地去做)。多处理版本似乎挂起了,因此需要进行更多的调整以查看它是否工作得更好。在您的示例中,
processing\u函数
返回的对象有多大?如果小于32 MB,您可以将外部循环的每个迭代剥离到自己的进程中。请查看多处理
模块。我将在上面的答案中附加一个使用它的示例。