理解Python线程bug

15

阅读http://bugs.python.org/msg160297,我可以看到Stephen White编写的一个简单脚本,演示了Python线程在遇到此异常时的错误。

Exception AttributeError: AttributeError("'_DummyThread' object has no attribute '_Thread__block'",) in <module 'threading' 

给出 Stephen White 的源代码(http://bugs.python.org/file25511/bad-thread.py),
import os
import thread
import threading
import time

def t():
    threading.currentThread() # Populate threading._active with a DummyThread
    time.sleep(3)

thread.start_new_thread(t, ())

time.sleep(1)

pid = os.fork()
if pid == 0:
    os._exit(0)

os.waitpid(pid, 0)

我们该如何重写它以解决这个错误?

在我看来,你可以将所有内容重写为 time.sleep(3)。我认为你应该明确说明重写后的程序实际要做什么。 - Janne Karila
3
这个程序仅仅是展示了一个Python的bug,如果你在Python 2.7版本上运行它,你会看到这个bug。请求是在不升级到修复这个bug的Python版本的情况下解决这个问题。 - user4815162342
1个回答

34

这个错误的出现是由于在一个外部线程上调用threading.currentThread()时,由threading API创建的虚拟线程对象之间的交互不良,以及在调用os.fork()后清理资源时调用的threading._after_fork函数。

为了解决这个错误而不修改Python源代码,可以使用monkey-patch将threading._DummyThread替换为__stop的无操作实现:

import threading
threading._DummyThread._Thread__stop = lambda x: 42

引起错误的原因最好在Richard Oudkerkcooyeah的评论中缩小范围。发生的情况如下:
  1. threading模块允许从非由threading API调用创建的线程中调用threading.currentThread()。然后它返回一个“虚拟线程”实例,该实例支持非常有限的Thread API子集,但仍然有助于识别当前线程。

  2. threading._DummyThreadThread的子类。Thread实例通常包含一个内部可调用项(self.__block),该项保留为实例分配的操作系统级锁的引用。由于所有可能使用self.__block的公共Thread方法都被_DummyThread覆盖,因此_DummyThread的构造函数有意通过删除self.__block来释放操作系统级锁。

  3. threading._after_fork打破了封装,并在所有已注册的线程上调用了私有的Thread.__stop方法,包括虚拟线程,在这些线程中,__stop从未被调用过。(它们不是由Python启动的,因此它们的停止也不受Python的管理。)由于虚拟线程不知道__stop,它们从Thread继承了它,并且该实现愉快地访问不存在于_DummyThread实例中的私有__block属性。最终,这种访问导致错误。

通过修改Thread.__stop以避免在删除__block时中断,已经在2.7分支中修复了该错误。3.x分支中,由于__stop被拼写为_stop并受到保护,因此通过覆盖_DummyThread_stop使其无效来解决该问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接