Python 线程与原子操作
我想实现一个线程,并且希望它有一个同步的 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,可能会去掉它。
另外要注意的是,任何真实的系统都应该以这种方式实现它。