Tornado ioloop 实例似乎跨进程共享

Tornado ioloop instance seems to be shared across processes

在多处理应用程序中,一个主进程产生多个子进程。每个进程都意味着 运行 它自己的 Tornado ioloop。但是,我注意到当进程启动时,IOLoop.current() 的所有实例(在主进程和所有子进程中)都是相同的。这难道不意味着 ioloop.spawn_callback(my_func) 运行 都在一个 ioloop 上下文中(在主进程中)吗?

这是我可以提取的最小示例:

from tornado.ioloop import IOLoop
import time
from multiprocessing import Process

def sub(i):
  print('sub %d: %s' % (i, hex(id(IOLoop.current(True)))))
  for i in range(10):
    time.sleep(1)


def main():
  print('main  ', hex(id(IOLoop.current(True))))

  for i in range(2):
    sub_process = Process(target=sub, args=(i, ))
    sub_process.daemon = True
    sub_process.start()

  time.sleep(5)

main()

输出:

main   0x7f14a09cf750
sub 0: 0x7f14a09cf750
sub 1: 0x7f14a09cf750

进程是否正确创建,并且不是预期的行为,会有多个 ioloop 实例?

Tornado 的 docs

中提到了这一点

it is important that nothing touches the global IOLoop instance (even indirectly) before the fork

您可以使用稍微修改的 main 函数获得您想要的行为:

def main():
    processes = []
    for i in range(2):
        process = Process(target=sub, args=(i,))
        process.daemon = True
        process.start()
        processes.append(process)
    print('main  ', hex(id(IOLoop.current(True))))
    time.sleep(5)

输出:

main   0x7fbd4ca0da30
sub 0: 0x7fbd4ca0db50
sub 1: 0x7fbd4ca0dc40

编辑

至于解释:分享是由于fork在Linux中的实现方式:using COW (copy-on-write);这意味着除非你在 child 进程中 写入共享 object,否则 parent 和 child 将共享相同的 object。一旦 child 修改了共享的 object 它将被 copiedchanged (这些更改不会在 parent) 中可见。