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

如何在Tornado中使用多进程处理阻塞调用以避免主线程阻塞

在Tornado中异步执行阻塞调用,避免主线程阻塞

你遇到的这个场景太典型了——Tornado作为单线程异步Web框架,一旦主线程被阻塞,整个服务就会卡住,所以必须把这类耗时的阻塞操作放到独立的线程或者进程里跑。下面给你两种实用的实现方案,直接就能套到你的代码里:

方案一:用线程池处理IO密集型阻塞操作

如果你的blocking_get是IO密集型(比如调用外部API、读写数据库这类),用线程池就足够了,开销比进程小很多。Tornado可以很好地和concurrent.futures.ThreadPoolExecutor配合:

import tornado.web
import tornado.gen
import time
from tornado.ioloop import IOLoop
from concurrent.futures import ThreadPoolExecutor

# 全局线程池,根据你的服务规模调整线程数
thread_pool = ThreadPoolExecutor(max_workers=4)

## 在独立线程中运行此函数
def blocking_get(var1):
    print("blocking function started")
    time.sleep(2)  # 模拟阻塞操作
    return {"res": f"result for {var1}"}

class RootHandler(tornado.web.RequestHandler):
    # Tornado 5+ 推荐用 async/await 写法
    async def get(self):
        var1 = self.get_argument("var1", default="default")
        # 把阻塞函数提交到线程池,await获取结果
        result = await IOLoop.current().run_in_executor(thread_pool, blocking_get, var1)
        self.write(result)

if __name__ == "__main__":
    app = tornado.web.Application([
        (r"/", RootHandler),
    ])
    app.listen(8888)
    print("Server started on http://localhost:8888")
    IOLoop.current().start()

这里的核心是IOLoop.current().run_in_executor(),它会把阻塞函数丢到指定的线程池里执行,主线程可以继续处理其他请求,等线程池里的任务完成后再返回结果。

方案二:用进程池处理CPU密集型阻塞操作

如果你的阻塞操作是CPU密集型(比如大量计算),那线程池受GIL限制效率不高,这时候就得用进程池:

import tornado.web
import tornado.gen
import time
from tornado.ioloop import IOLoop
from concurrent.futures import ProcessPoolExecutor

# 全局进程池,进程数建议和CPU核心数匹配
process_pool = ProcessPoolExecutor(max_workers=2)

## 在独立进程中运行此函数
def blocking_get(var1):
    print("blocking function started in process")
    time.sleep(2)  # 模拟CPU密集型操作(实际可能是复杂计算)
    return {"res": f"result for {var1}"}

class RootHandler(tornado.web.RequestHandler):
    async def get(self):
        var1 = self.get_argument("var1", default="default")
        # 提交到进程池执行
        result = await IOLoop.current().run_in_executor(process_pool, blocking_get, var1)
        self.write(result)

if __name__ == "__main__":
    app = tornado.web.Application([
        (r"/", RootHandler),
    ])
    app.listen(8888)
    print("Server started on http://localhost:8888")
    IOLoop.current().start()

注意:进程之间的参数和返回值必须是可序列化的(比如字典、字符串、数字这些),如果要传递复杂对象,得确保它们能被pickle序列化。

额外提示

  • 线程池/进程池的大小要根据你的服务负载调整,线程池适合IO密集,进程池适合CPU密集,别盲目开太多,不然会导致资源耗尽。
  • 如果你的Tornado版本低于5.0,那得用@tornado.gen.coroutine装饰器,配合yield来替代async/await,写法大概是这样:
class RootHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        var1 = self.get_argument("var1", default="default")
        result = yield IOLoop.current().run_in_executor(thread_pool, blocking_get, var1)
        self.write(result)

这样处理后,你的主线程就永远不会被阻塞,多个请求可以并行处理啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:59:14