保护多线程程序中的临界区

Guarding critical section in a multithreaded program

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

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

您无法保证主线程会在正确的时间检查 _critical_section 的值以查看 False 值。在主线程再次检查该值之前,工作线程可能会离开并 re-enter 临界区。这可能不会导致任何正确性问题,但它可能会导致您的程序需要更长的时间才能关闭(因为当主线程 "misses" 安全时间关闭时,它将不得不等待另一个关键部分完成)。

此外,工作线程可能 re-enter 在主线程注意到 _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 将阻塞直到线程完成。这样可以确保您不会打扰 mid-critical-section.