我有一个Thread应该等待任务从不同的多线程到达并执行它们直到没有剩下任务。如果没有剩下任务,它应该再次等待。
我尝试了这个类(只有相关的代码):
from threading import Event, Thread
class TaskExecutor(object):
def __init__(self):
self.event = Event()
self.taskInfos = []
self._running = True
task_thread = Thread(target=self._run_worker_thread)
self._running = True
task_thread.daemon = True
task_thread.start()
def _run_worker_thread(self):
while self.is_running():
if len(self.taskInfos) == 0:
self.event.clear()
self.event.wait()
try:
msg, task = self.taskInfos[0]
del self.taskInfos[0]
if task:
task.execute(msg)
except Exception, e:
logger.error("Error " + str(e))
def schedule_task(self, msg, task):
self.taskInfos.append((msg, task))
self.event.set()
多个线程在每次添加任务时都在调用schedule_task
。
问题是我有时会在list index out of range
行中发出错误msg, task = self.taskInfos[0]
。下面的del self.taskInfos[0]
是我删除任务的唯一选项。
怎么会发生这种情况?我觉得我必须synchronize
所有内容,但python中没有这样的关键字,阅读文档会带来这种模式。
答案 0 :(得分:3)
这段代码非常无望 - 放弃它并做一些理智的事情;-)什么是理智的?使用Queue.Queue
。那是为了做你想做的事。
替换:
self.event = Event()
self.taskInfos = []
使用:
self.taskInfos = Queue.Queue()
(当然你也必须import Queue
。)
添加任务:
self.taskInfos.put((msg, task))
要完成任务:
msg, task = self.taskInfos.get()
这将阻止任务可用。还可以选择执行非阻塞.get()
尝试,并在超时时执行.get()
尝试(请阅读文档)。
尝试修复您拥有的代码将是一场噩梦。从本质上讲,Event
的功能不足以在此上下文中执行线程安全所需的操作。事实上,每当你看到代码正在执行Event.clear()
时,它的可能错误(受比赛影响)。
编辑:接下来会出现什么问题
如果您继续尝试修复此代码,则可能会发生以下情况:
the queue is empty
thread 1 does len(self.taskInfo) == 0, and loses its timeslice
thread 2 does self.taskInfos.append((msg, task))
and does self.event.set()
and loses its timeslice
thread 1 resumes and does self.event.clear()
and does self.event.wait()
糟糕!现在线程1永远等待,尽管队列中的任务 。
这就是Python提供Queue.Queue
的原因。使用虚弱的Event
,你极不可能得到正确的解决方案。
答案 1 :(得分:2)
可以使用以下序列(假设线程#0 是使用者并运行您的_run_worker_thread
方法,并且线程线程#1 和线程#2 是生产者并调用schedule_task
方法):
schedule_task
,在set
schedule_task
并到达set
set
电话粗体部分是理解可能的种族的关键。基本上,工作线程可以使用所有任务,在if len(self.taskInfos) == 0
条件为False
之前旋转,然后所有生成器在追加到队列后将设置为set
事件。
可能的解决方案包括wait
之后根据xndrme的评论中的建议再次检查条件,或者使用Lock
类,最好的一个可能是提到的Queue.Queue
类Tim Peters中的his answer。