【问题标题】:Python threads and atomic operationsPython 线程和原子操作
【发布时间】:2014-04-17 10:16:58
【问题描述】:

我想用同步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 中吗?这个实现正确吗?我的目标不是完全避免事件或锁,只是想了解这个原子操作的东西。

【问题讨论】:

    标签: python multithreading


    【解决方案1】:

    据我所知,它在 Python 中也是不正确的,因为 _working 仍然可以放入寄存器中或以其他方式优化,或者可能发生其他一些事情会改变它的值。读写这个字段可以被处理器任意重新排序。

    好吧,假设在多线程世界中,您不应该真正问:为什么不应该工作,而应该问为什么保证可以工作

    话虽如此,在大多数情况下,多线程在 CPython 中更容易一些,因为 GIL 保证:

    • 在任何给定时间只执行一个解释器命令。
    • 经常强制线程之间的内存同步。

    请记住,GIL 是一个实现细节,如果有人在没有它的情况下重写 CPython,它可能会消失。

    还要注意,它应该在任何实际系统中以这种方式实现它。

    【讨论】:

    • “为什么这可以保证工作。”很好,谢谢。
    • 是时候重新审视这个答案了吗?我很确定 Python 不能“优化”属性访问。
    【解决方案2】:

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

    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()
    

    当然,如果你知道 worker 从不睡觉,你可以删除 self.cond 的创建和所有使用,保留其余代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多