我正在运行一个即将终止的线程,但在那时,我已经想要摆脱它的引用。我可以开始并希望最好,还是应该以某种特殊的方式处理它?</ p>
class CoolThing(object):
def __init__(self):
self.thread = None
def run_in_background(self, callback, period=0.5):
if self.thread:
raise RuntimeError
def worker():
worker.running = True
while worker.running:
if some_event():
callback(self)
time.sleep(period)
self.thread = (threading.Thread(target=worker), worker)
self.thread[0].start()
def stop_background(self, join=False):
if not self.thread:
raise RuntimeError
# Make the worker function end harmfully.
self.thread[1].running = False
if join:
self.thread[0].join()
# What should I now do with the thread being about to
# terminate, when not being joined?
# ...
self.thread = None
答案 0 :(得分:4)
当留下while循环时,你应该在worker
内引发的回调中将线程设置为None:
修改:现在还支持立即重启后台进程
import time
import threading
class CoolThing(object):
def __init__(self):
self.thread = None
def run_in_background(self, callback, period=0.5):
wait_count = 0
while True:
if self.thread:
if self.thread[1].running or wait_count>10:
raise RuntimeError()
time.sleep(0.5)
wait_count += 1
else:
break
def worker():
t0 = time.time()
worker.running = True
while worker.running:
if time.time()-t0>2:
callback()
t0 = time.time()
time.sleep(period)
worker.callback()
worker.callback = self.dispose
self.thread = (threading.Thread(target=worker), worker)
self.thread[0].start()
def stop_background(self, join=False):
if not self.thread:
raise RuntimeError
self.thread[1].running = False
if join:
self.thread[0].join()
self.stopping = True
def dispose(self):
self.thread = None
self.stopping
def my_callback():
print "Beep"
if __name__=="__main__":
cool_thing = CoolThing()
cool_thing.run_in_background(my_callback, 0.5)
time.sleep(10)
cool_thing.stop_background()
# Immediatley restart process
cool_thing.run_in_background(my_callback, 0.5)
time.sleep(10)
cool_thing.stop_background()
print cool_thing.thread
time.sleep(3)
print cool_thing.thread
给出输出:
Beep
Beep
Beep
(<Thread(Thread-2, started 10760)>, <function worker at 0x02DEDD70>)
None
所以在调用stop_background之后,self.thread仍然设置,但稍后,它是None
。您还可以保存worker.callback-variable并按名称调用dispose(),但这样,代码更灵活。
编辑2 :新要求,新代码示例
我为工人(SRP)制作了一个单独的班级,而CoolThing则列出了这样的工人名单。如果run_background(...)
已启动,则会检查是否有任何工作程序仍在运行(未请求停止),然后引发RuntimeError。否则,启动一名新员工。
stop_background()
告诉每个worker停止,每个worker调用一个回调,然后从所有worker的列表中删除该worker。
import time
import threading
class Worker(threading.Thread):
def __init__(self, callback, period=0.5, finished_callback = None):
threading.Thread.__init__(self)
self.callback = callback
self.period = period
self._stop_requested = False
self._finished_callback = finished_callback
def run(self):
t0 = time.time()
while not self._stop_requested:
if time.time()-t0>2:
self.callback()
t0 = time.time()
time.sleep(self.period)
if self._finished_callback:
self._finished_callback(self)
def request_stop(self):
self._stop_requested = True
@property
def stopping(self):
return self._stop_requested
class CoolThing(object):
def __init__(self):
self.workers = []
self.workers_lock = threading.Lock()
def run_in_background(self, callback, period=0.5):
if len([w for w in self.workers if not w.stopping])>0:
raise RuntimeError()
worker = Worker(callback, period, finished_callback=self.dispose)
with self.workers_lock:
self.workers.append(worker)
worker.start()
def stop_background(self, join=False):
if len(self.workers) == 0:
raise RuntimeError()
for worker in self.workers:
worker.request_stop()
if join:
for worker in self.workers:
worker.join()
def dispose(self, worker):
with self.workers_lock:
self.workers.remove(worker)
def my_callback():
print "Beep"
if __name__=="__main__":
cool_thing = CoolThing()
cool_thing.run_in_background(my_callback, 0.5)
time.sleep(10)
print cool_thing.workers
cool_thing.stop_background()
# Immediatley restart process
cool_thing.run_in_background(my_callback, 0.5)
print cool_thing.workers
time.sleep(5)
print cool_thing.workers
time.sleep(5)
cool_thing.stop_background()
print cool_thing.workers
time.sleep(3)
print cool_thing.workers
答案 1 :(得分:0)
作为一个已经提到过的评论者,你不能使用join()
,因为所有这一切都是等待线程自然终止。虽然底层的线程库通常只有一个(例如pthread_kill()
),但我不知道任何强制终止线程的本机Python API。这通常是因为在Python中强行杀死一个线程通常是一件非常糟糕的事情。
但是,在这种情况下,您似乎没有尝试杀死不合作的线程,您已向线程发出信号,要求它正常终止(通过设置running
属性)。
我没有看到任何理由您不能立即将thread
设置为None
- 我不相信删除对线程对象的最后一个引用实际上会导致任何问题因为线程将一直保持活动状态,直到它终止(除非你在其上设置daemon
到True
并且主程序终止)。当然,在定义worker()
函数时创建的闭包中的任何对象仍然存在,因此在线程终止之前它们不会被释放(甚至可能不是闭包设置的方式 - 我需要更仔细地考虑一下。)
无论如何,我认为如果你只是在线程终止时安排join()
,你的生活会更容易。如果你担心的是你必须等待你的线程的时间超时,你可以使用threading.Condition
对象来解决这个问题:
class CoolThing(object):
def __init__(self):
self.thread = None
self.exit_condition = threading.Condition()
def run_in_background(self, callback, period=0.5):
if self.thread:
raise RuntimeError
def worker(exit_condition):
exit_condition.acquire()
worker.running = True
while worker.running:
if some_event():
callback(self)
exit_condition.wait(period)
exit_condition.release()
self.thread = (threading.Thread(target=worker, args=(self.exit_condition,)),
worker)
self.thread[0].start()
def stop_background(self):
if not self.thread:
raise RuntimeError
# Make the worker function end harmfully.
self.exit_condition.acquire()
self.thread[1].running = False
self.exit_condition.notify()
self.exit_condition.release()
self.thread[0].join()
self.thread = None