我有一个多线程Python程序(金融交易),其中某些线程执行关键部分(比如在执行交易的过程中)。执行关键部分的线程是守护程序线程。程序的主线程捕获SIGINT
并尝试通过释放子线程持有的所有资源来优雅地退出程序。为了防止导致子线程突然终止的主线程;主线程将循环遍历子线程对象列表并调用它们的shutdown()
函数。在返回之前,此函数将阻塞直到线程的关键部分完成。
以下是基本实现
class ChildDaemonThread(Thread):
def __init__(self):
self._critical_section = False
# other initialisations
def shutdown(self):
# called by parent thread before calling sys.exit(0)
while True:
if not self._critical_section:
break
# add code to prevent entering critical section
# do resource deallocation
def do_critical_stuff(self):
self._critical_section = True
# do critical stuff
self._critical_section = False
def run(self):
while True:
self._do_critical_stuff()
我不确定我的实现是否有效,因为ChildDaemonThread
正在通过do_critical_stuff()
执行关键部分,如果父线程调用子shutdown()
,则阻塞直到关键部分执行,然后在此时同时调用ChildDaemonThread
run()
和do_critical_stuff()
两种方法(我不确定这是否合法)。这可能吗?我的实施是否正确?有没有更好的方法来实现这一目标?
答案 0 :(得分:2)
此实施中存在一些竞争条件。
您无法保证主线程会在合适的时间检查_critical_section
的值以查看False
值。在主线程再次检查该值之前,工作线程可能会离开并重新进入临界区。这可能不会导致任何正确性问题,但它可能导致程序需要更长时间才能关闭(因为当主线程“错过”安全时间关闭它时,必须等待另一个关键部分完成)。
此外,在主线程注意到_critical_section
为False
但主线程设法导致进程到之前,工作线程可能重新进入关键字出口。这可能会造成真正的正确性问题,因为它会有效地破坏您确保关键部分完成的尝试。
当然,由于其他一些问题,该程序也可能会崩溃。因此,如果您实现从中断的临界区恢复的能力可能会更好。
但是,如果你想尽可能地改进这个策略,我会建议更像这样的事情:
class ChildDaemonThread(Thread):
def __init__(self):
self._keep_running = True
# other initialisations
def shutdown(self):
# called by parent thread before calling sys.exit(0)
self._keep_running = False
def do_critical_stuff(self):
# do critical stuff
def run(self):
while self._keep_running:
self._do_critical_stuff()
# do resource deallocation
workers = [ChildDaemonThread(), ...]
# Install your SIGINT handler which calls shutdown on all of workers
# ...
# Start all the workers
for w in workers:
w.start()
# Wait for the run method of all the workers to return
for w in workers:
w.join()
这里的关键是join
将阻塞,直到线程结束。这可以确保您不会中断一个中间关键部分。