保护多线程程序中的关键部分

时间:2018-03-21 13:39:31

标签: python multithreading critical-section

我有一个多线程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()两种方法(我不确定这是否合法)。这可能吗?我的实施是否正确?有没有更好的方法来实现这一目标?

1 个答案:

答案 0 :(得分:2)

此实施中存在一些竞争条件。

您无法保证主线程会在合适的时间检查_critical_section的值以查看False值。在主线程再次检查该值之前,工作线程可能会离开并重新进入临界区。这可能不会导致任何正确性问题,但它可能导致程序需要更长时间才能关闭(因为当主线程“错过”安全时间关闭它时,必须等待另一个关键部分完成)。

此外,在主线程注意到_critical_sectionFalse但主线程设法导致进程到之前,工作线程可能重新进入关键字出口。这可能会造成真正的正确性问题,因为它会有效地破坏您确保关键部分完成的尝试。

当然,由于其他一些问题,该程序也可能会崩溃。因此,如果您实现从中断的临界区恢复的能力可能会更好。

但是,如果你想尽可能地改进这个策略,我会建议更像这样的事情:

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将阻塞,直到线程结束。这可以确保您不会中断一个中间关键部分。