You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tornado服务器启动后阻塞脚本执行问题求助

解决Tornado IOLoop.start()阻塞脚本的问题

嘿,别担心,这绝对不是什么“常识”问题——刚接触Tornado的时候几乎所有人都会踩这个坑😉。我来给你拆解一下原因和解决方案:

为什么会阻塞?

IOLoop.start()本质是一个无限事件循环,它会一直占用当前线程(通常是主线程)处理网络请求和事件,所以在它之后的代码根本没机会被执行。这是Tornado异步模型的核心特点,但对新手来说确实容易困惑。

解决方案(按场景选)

场景1:其他任务是轻量、非阻塞的

如果你的其他任务不需要长时间占用CPU/IO(比如打印日志、简单计算),可以把任务添加到Tornado的事件循环中,让它和服务器一起在事件循环里执行:

import tornado.web
import tornado.ioloop

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, Tornado!")

def my_other_task():
    print("执行非阻塞的其他任务啦!")
    # 这里写你的任务逻辑,比如定时打印状态

def main():
    app = tornado.web.Application([(r"/", MainHandler)])
    app.listen(8888)
    
    # 在启动事件循环前,把任务加入循环队列
    tornado.ioloop.IOLoop.current().add_callback(my_other_task)
    
    # 如果需要定时重复执行任务,可以用add_timeout
    # def repeat_task():
    #     print("重复执行任务")
    #     tornado.ioloop.IOLoop.current().add_timeout(tornado.ioloop.IOLoop.current().time() + 5, repeat_task)
    # tornado.ioloop.IOLoop.current().add_callback(repeat_task)
    
    # 启动事件循环(这一步会阻塞,但任务已经在队列里了)
    tornado.ioloop.IOLoop.current().start()

if __name__ == "__main__":
    main()

场景2:其他任务是阻塞型的(比如数据库查询、文件IO)

如果你的任务是耗时的阻塞操作(比如time.sleep()、同步数据库调用),直接放到事件循环里会拖慢服务器响应。这时候可以用线程池把阻塞任务放到后台线程执行:

import tornado.web
import tornado.ioloop
from concurrent.futures import ThreadPoolExecutor

# 创建线程池,最多同时运行4个线程
executor = ThreadPoolExecutor(max_workers=4)

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, Tornado!")

def blocking_task():
    # 模拟耗时阻塞操作,比如读取大文件、同步查询数据库
    import time
    time.sleep(3)
    print("耗时阻塞任务执行完成!")

# 用async/await包装阻塞任务,让它能被事件循环处理
async def run_blocking_task():
    await tornado.ioloop.IOLoop.current().run_in_executor(executor, blocking_task)

def main():
    app = tornado.web.Application([(r"/", MainHandler)])
    app.listen(8888)
    
    # 将异步任务加入事件循环
    tornado.ioloop.IOLoop.current().add_callback(run_blocking_task)
    
    tornado.ioloop.IOLoop.current().start()

if __name__ == "__main__":
    main()

场景3:必须让主线程执行其他任务

如果你的核心任务需要在主线程运行,而Tornado服务器只是辅助功能,可以把服务器放到子线程中启动:

import tornado.web
import tornado.ioloop
import threading

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, Tornado!")

def start_tornado_server():
    app = tornado.web.Application([(r"/", MainHandler)])
    app.listen(8888)
    # 子线程中启动事件循环
    tornado.ioloop.IOLoop.current().start()

def main_task():
    print("主线程开始执行核心任务...")
    # 这里写你的主线任务逻辑,比如定时检查系统状态
    import time
    while True:
        time.sleep(2)
        print("主线程任务正在运行中")

if __name__ == "__main__":
    # 创建并启动Tornado子线程
    tornado_thread = threading.Thread(target=start_tornado_server)
    tornado_thread.daemon = True  # 设置为守护线程,主线程退出时自动关闭服务器
    tornado_thread.start()
    
    # 主线程执行核心任务
    main_task()

总结

  • 轻量任务用add_callback加入事件循环
  • 阻塞任务用run_in_executor配合线程池
  • 主线程需要跑任务就把服务器放子线程

内容的提问来源于stack exchange,提问作者Jan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:00:14