Python 线程与原子操作

8 投票
2 回答
8693 浏览
提问于 2025-04-18 03:06

我想实现一个线程,并且希望它有一个同步的 stop() 方法。

我见过这样的版本:

class Thread1:
    def __init__(self):
        self._stop_event = threading.Event()
        self._thread = None

    def start(self):
        self._thread = threading.Thread(target=self._run)
        self._thread.start()

    def stop(self):
        self._stop_event.set()
        self._thread.join()

    def _run(self):
        while not self._stop_event.is_set():
            self._work()

    def _work(self):
        print("working")

但是我读到过原子操作是线程安全的,感觉可以不使用 Event 来实现。所以我想出了这个:

class Thread2:
    def __init__(self):
        self._working = False
        self._thread = None

    def start(self):
        self._working = True
        self._thread = threading.Thread(target=self._run)
        self._thread.start()

    def stop(self):
        self._working = False
        self._thread.join()

    def _run(self):
        while self._working:
            self._work()

    def _work(self):
        print("working")

我觉得在C语言中,类似的实现可能被认为是不正确的,因为编译器可能会把 _working 放到寄存器中(甚至可能优化掉),这样工作线程就永远不知道这个变量已经改变了。在Python中会发生这样的情况吗?这个实现正确吗?我并不是想完全避免使用事件或锁,只是想理解一下原子操作的概念。

2 个回答

1

这里有一个更全面的解决方案,这个方案也可以用在工作线程有时需要延迟的情况。

class Worker(threading.Thread):
    quit = False

    def __init__(self, ...):
        super().__init__()
        self.cond = threading.Condition()
        ...

    def delay(self, seconds):
        deadline = time.monotonic() + seconds
        with self.cond:
            if self.quit:
                raise SystemExit()
            if time.monotinic() >= deadline:
                return
            self.cond.wait(time.monotonic() - deadline)

    def run(self):
        while not self.quit:
            # work here
            ...

            # when delay is needed
            self.delay(123)

    def terminate(self):
        with self.cond:
            self.quit = True
            self.cond.notify_all()
        self.join()

使用方法如下:

worker = Worker()
worker.start()
...
# finally
worker.terminate()

当然,如果你确定这个工作线程从来不会休眠,你可以去掉创建和所有使用 self.cond 的部分,其他代码可以保留。

2

根据我的理解,在Python中这也是不正确的,因为_working 仍然可以被放入寄存器中,或者以其他方式进行优化,或者可能发生其他事情导致它的值发生变化。处理器可以随意重新排序对这个字段的读取和写入。

那么在多线程的世界里,你其实不应该问:为什么这不应该工作,而是应该问:为什么这能保证工作

话虽如此,在大多数情况下,在CPython中多线程会稍微简单一些,因为有一个叫做GIL的东西,它保证了:

  • 在任何时刻,只会执行一个解释器命令。
  • 强制线程之间的内存同步。

请记住,GIL是一个实现细节,如果有人重写了CPython,可能会去掉它。

另外要注意的是,任何真实的系统都应该以这种方式实现它。

撰写回答