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
相关产品推荐
相关产品推荐

