我创建了自己的线程类,将在线程池中使用,如下所示:
class SendFilesThread(threading.Thread):
execute = True
def __init__(self, port, rate, fileQueue):
self.daemon = True
self.fileQueue = fileQueue
#Other initialization
def run(self):
while SendFilesThread.execute == True or not self.fileQueue.empty():
self.filename = self.fileQueue.get()
#some file processing
self.fileQueue.task_done()
@staticmethod
def terminate():
SendFilesThread.execute = False
在程序的主线程中,在处理完所有文件后,我尝试按如下方式关闭线程池:
SendFilesThread.terminate()
for t in threadPool:
if t.is_alive():
t.join()
exit()
我的理解是,如果我调用join()
,它将阻止调用线程(在本例中为主线程)继续,直到连接的线程完成处理。我的问题是,尽管线程完成,但它永远不会返回主线程,程序就会挂起。关闭我的线程池时我做错了吗?
答案 0 :(得分:2)
有两件事。首先,从设计的角度来看,让execute
标志位于类级别有点奇怪。将它作为每个类实例上的标志通常会更好。
其次,你有竞争条件。 execute
标志由多个线程访问,并且不受同步原语(如互斥锁)的保护。这意味着{/ 1}}调用可以在任何工作线程检查标志之后运行(并设置标志),但之前该线程尝试将下一个队列出列文件名。因为您在没有超时的情况下调用terminate()
,所以工作线程将挂起,主线程将在get()
调用中阻塞。随之而来的是死锁。
有很多方法可以解决这个问题。您可以使用线程同步原语(如互斥锁)来保护t.join()
标志,或使用execute
对象代替简单的布尔值。
另一个,在我看来更简单,解决方案是发送一个" sentinel"同一队列上的值,表示该线程应该退出。看起来您正在发送字符串文件名,因此空字符串可能是一个不错的选择。 (threading.Event
也常用。)
现在每个线程的工作循环如下所示:
None
主线程不是def run(self):
while True:
self.filename = self.fileQueue.get()
if self.filename == '':
return
# Process file
静态方法,而是为每个工作线程放置一个空字符串(即terminate()
)。
答案 1 :(得分:1)
如果fileQueue
是multiprocessing.Queue
或queue.Queue
,则其.get()
方法将阻止,直到某些内容到达队列。尝试使用.get_nowait()
,但如果要在队列为空时继续执行,则可能必须将其包装在try块中。
try:
self.filename = self.fileQueue.get_nowait()
except queue.Empty:
time.sleep(1)
请参阅queue docs了解更多