让我们假设我使用Python 2.6,并且无法升级(即使这会有所帮助)。我编写了一个使用Queue类的程序。我的制作人是一个简单的目录列表。我的消费者线程从队列中提取文件,然后用它做任务。如果文件已经处理过,我跳过它。处理后的列表是在所有线程启动之前生成的,所以它不是空的。
这是一些伪代码。
import Queue, sys, threading
processed = []
def consumer():
while True:
file = dirlist.get(block=True)
if file in processed:
print "Ignoring %s" % file
else:
# do stuff here
dirlist.task_done()
dirlist = Queue.Queue()
for f in os.listdir("/some/dir"):
dirlist.put(f)
max_threads = 8
for i in range(max_threads):
thr = Thread(target=consumer)
thr.start()
dirlist.join()
我得到的一个奇怪的行为是,如果一个线程遇到一个已经处理过的文件,那么该线程会停止并等待整个程序结束。我做了一些测试,前7个线程(假设8是最大值)停止,而第8个线程继续处理,一次一个文件。但是,通过这样做,我失去了线程化应用程序的全部理由。
我做错了什么,或者这是Python 2.6中Queue / threading类的预期行为?
答案 0 :(得分:2)
我尝试运行您的代码,但没有看到您描述的行为。但是,程序永远不会退出。我建议更改.get()
电话,如下所示:
try:
file = dirlist.get(True, 1)
except Queue.Empty:
return
如果您想知道当前正在执行哪个线程,可以导入thread模块并打印thread.get_ident()。
我在.get()
:
print file, thread.get_ident()
并获得以下输出:
bin 7116328
cygdrive 7116328
cygwin.bat 7149424
cygwin.ico 7116328
dev etc7598568
7149424
fix 7331000
home 7116328lib
7598568sbin
7149424Thumbs.db
7331000
tmp 7107008
usr 7116328
var 7598568proc
7441800
输出很乱,因为线程同时写入stdout。各种线程标识符进一步确认所有线程都在运行。
在实际代码或测试方法中可能有问题,但在您发布的代码中却没有?
答案 1 :(得分:1)
由于此问题仅在查找已经处理过的文件时才会显现,因此这似乎与processed
列表本身有关。你试过实现一个简单的锁吗?例如:
processed = []
processed_lock = threading.Lock()
def consumer():
while True:
with processed_lock.acquire():
fileInList = file in processed
if fileInList:
# ... et cetera
线程往往会导致最奇怪的错误,即使它们似乎“不应该”发生。在共享变量上使用锁定是确保您不会遇到可能导致线程死锁的某种竞争条件的第一步。
当然,如果你在# do stuff here
下所做的事情是CPU密集型的,那么由于全局解释器锁,Python一次只能从一个线程运行代码。在这种情况下,您可能希望切换到multiprocessing
模块 - 它与threading
非常相似,但您需要使用其他解决方案替换共享变量(有关详细信息,请参阅here)。