这已经讨论了很多很多次了,但我仍然没有很好地掌握如何最好地实现这一点。
假设我有两个线程:一个主应用线程和一个工作线程。主应用程序线程(比如说它是一个 WXWidgets GUI 线程,或者一个正在循环并在控制台上接受用户输入的线程)可能有理由停止工作线程 - 用户正在关闭应用程序,单击停止按钮,一些错误发生在主线程中,无论如何。
通常建议是设置一个标志,线程经常检查以确定是否退出。但是,我对解决此问题的建议方法有两个问题:
首先,在我的代码中不断地检查标志会使我的代码非常难看,而且由于大量的代码重复,它非常非常容易出现问题。举个例子:
def WorkerThread():
while (True):
doOp1() # assume this takes say 100ms.
if (exitThread == True):
safelyEnd()
return
doOp2() # this one also takes some time, say 200ms
if (exitThread == True):
safelyEnd()
return
if (somethingIsTrue == True):
doSomethingImportant()
if (exitThread == True): return
doSomethingElse()
if (exitThread == True): return
doOp3() # this blocks for an indeterminate amount of time - say, it's waiting on a network respond
if (exitThread == True):
safelyEnd()
return
doOp4() # this is doing some math
if (exitThread == True):
safelyEnd()
return
doOp5() # This calls a buggy library that might block forever. We need a way to detect this and kill this thread if it's stuck for long enough...
saveSomethingToDisk() # might block while the disk spins up, or while a network share is accessed...whatever
if (exitThread == True):
safelyEnd()
return
def safelyEnd():
cleanupAnyUnfinishedBusiness() # do whatever is needed to get things to a workable state even if something was interrupted
writeWhatWeHaveToDisk() # it's OK to wait for this since it's so important
如果我添加更多代码或更改代码,我必须确保我在所有地方都添加了这些检查块。如果我的工作线程是一个非常长的线程,我可以轻松地进行数十甚至数百次这样的检查。很麻烦。
想想其他的问题。如果 doOp4() 确实意外死锁,我的应用程序将永远旋转并且永远不会退出。用户体验不好!
使用守护线程也不是一个好的选择,因为它剥夺了我执行safelyEnd()
代码的机会。这段代码可能很重要——刷新磁盘缓冲区、为调试目的写入日志数据等。
其次,我的代码可能会调用阻止我没有机会经常检查的函数。假设这个函数存在,但它在我无权访问的代码中 - 比如说库的一部分:
def doOp4():
time.sleep(60) # imagine that this is a network thread, that waits for 60 seconds for a reply before returning.
如果该超时为 60 秒,即使我的主线程发出结束线程的信号,它仍然可能会在那里停留 60 秒,此时停止等待网络响应并退出是完全合理的。但是,如果该代码是我没有编写的库的一部分,那么我无法控制它是如何工作的。
即使我确实为网络检查编写了代码,我基本上也必须对其进行重构,这样它就不会等待 60 秒,而是循环 60 次并在检查退出线程之前等待 1 秒!再次,非常混乱!
所有这一切的结果是,感觉能够轻松实现这一点的好方法是以某种方式导致特定线程上的异常。如果我能做到这一点,我可以将整个工作线程的代码包装在一个 try 块中,并将safelyEnd()
代码放在异常处理程序中,甚至是一个finally
块中。
有没有办法做到这一点,或者用不同的技术重构这段代码,让事情顺利进行?理想情况下,当用户请求退出时,我们希望让他们等待尽可能少的时间。似乎必须有一种简单的方法来实现这一点,因为这在应用程序中是很常见的事情!
大多数线程通信对象不允许这种类型的设置。他们可能允许以一种更简洁的方式来设置退出标志,但它仍然不能消除不断检查退出标志的需要,并且它仍然不会处理由于外部调用或因为它只是在一个繁忙的循环。
对我来说最重要的是,如果我有一个很长的工作线程过程,我必须用数百次检查标志来乱扔它。这看起来太乱了,感觉不是很好的编码实践。一定有更好的方法...
任何建议将不胜感激。