tornado 中的IOLoop 有哪些方法
以下是 Tornado 中 IOLoop 核心方法的详细说明、使用示例、适用场景及最佳实践,采用模块化分类呈现:
一、事件循环控制方法
1. start()
作用:启动事件循环(阻塞调用)
场景:主程序入口
def start_server():
app = Application([(r"/", MainHandler)])
app.listen(8888)
IOLoop.current().start() # 阻塞直到调用stop()
最佳实践:
-
始终在
if __name__ == "__main__":中调用 -
配合
try/finally确保资源清理
2. stop()
作用:停止事件循环
场景:优雅关闭服务
def graceful_shutdown():
loop = IOLoop.instance()
print("Shutting down in 3s...")
loop.call_later(3, loop.stop) # 延迟停止
loop.start()
最佳实践:
-
在信号处理中调用(如
SIGINT) -
避免在回调中直接
stop(),应延迟调用
3. close(all_fds=False)
作用:释放所有资源
场景:程序退出清理
try:
IOLoop.current().start()
except KeyboardInterrupt:
pass
finally:
IOLoop.instance().close(all_fds=True) # 强制关闭所有fd
注意:all_fds=True 可能关闭非Tornado管理的文件描述符
二、回调调度方法
1. add_callback(callback, *args, **kwargs)
作用:线程安全添加回调到下次循环
场景:跨线程通知主线程
def thread_to_main():
def worker_thread():
IOLoop.instance().add_callback(
lambda: print("Called from worker thread"))
threading.Thread(target=worker_thread).start()
最佳实践:
-
优先于
call_soon()(两者功能相同) -
避免传递复杂对象(可能引发序列化问题)
2. call_later(delay, callback, *args, **kwargs)
作用:延迟执行回调(秒为单位)
场景:定时任务、超时控制
def set_timeout():
def on_timeout():
print("Timeout reached!")
IOLoop.current().call_later(5, on_timeout) # 5秒后触发
替代方案:
# 更精确的绝对时间调度
deadline = time.time() + 5
IOLoop.current().call_at(deadline, on_timeout)
3. call_at(when, callback, *args, **kwargs)
作用:在指定时间戳执行回调
场景:需要高精度定时
def schedule_at():
run_time = time.time() + 10 # 10秒后
IOLoop.current().call_at(
run_time,
lambda: print("Run at exact time")
)
最佳实践:
-
使用
time.time()而非time.monotonic() -
对高频任务考虑合并回调
三、I/O 事件监控方法
1. add_handler(fd, handler, events)
作用:监控文件描述符事件
事件类型:IOLoop.READ / IOLoop.WRITE / IOLoop.ERROR
场景:自定义协议实现
def watch_socket():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', 9000))
sock.listen(1)
sock.setblocking(False)
def connection_handler(fd, events):
conn, addr = sock.accept()
print(f"New connection: {addr}")
loop = IOLoop.current()
loop.add_handler(
sock.fileno(),
connection_handler,
IOLoop.READ
)
最佳实践:
-
始终配合
setblocking(False)使用 -
使用
fileno()获取文件描述符
2. update_handler(fd, events)
作用:修改已监控fd的事件类型
场景:动态调整监听事件
def modify_events():
def handler(fd, events):
if events & IOLoop.READ:
data = os.read(fd, 1024)
if data:
# 有数据后改为监控写事件
IOLoop.current().update_handler(fd, IOLoop.WRITE)
loop.add_handler(rfd, handler, IOLoop.READ)
3. remove_handler(fd)
作用:移除文件描述符监控
场景:连接关闭时清理资源
def cleanup(fd):
loop = IOLoop.current()
loop.remove_handler(fd)
os.close(fd) # 记得关闭fd
四、线程集成方法
1. run_in_executor(executor, func, *args)
作用:在线程池执行阻塞函数
场景:CPU密集型/阻塞IO操作
async def query_database():
loop = IOLoop.current()
# 使用默认线程池
result = await loop.run_in_executor(
None, # 使用默认executor
blocking_db_query,
"SELECT * FROM table"
)
return result
最佳实践:
-
复用线程池(避免频繁创建)
-
限制并发量(通过
ThreadPoolExecutor参数)
2. run_sync(func)
作用:同步运行协程并返回结果
场景:主程序入口
def main():
async def init_app():
app = make_app()
app.listen(8888)
await asyncio.Event().wait() # 永久运行
IOLoop.current().run_sync(init_app)
五、高级模式与最佳实践
1. 定时任务管理器
class Timer:
def __init__(self):
self._loop = IOLoop.current()
self._timers = {}
def add_interval(self, name, callback, interval_ms):
def wrapper():
callback()
self._timers[name] = self._loop.call_later(
interval_ms / 1000, wrapper)
self._timers[name] = self._loop.call_later(
interval_ms / 1000, wrapper)
def cancel(self, name):
if name in self._timers:
self._loop.remove_timeout(self._timers[name])
2. 异常安全回调
def safe_callback(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
logging.error("Callback failed: %s", e)
return wrapped
# 使用装饰器
loop.add_callback(safe_callback(risky_operation))
3. 性能敏感场景优化
# 合并多个回调
def batch_updates():
updates = []
def process_batch():
nonlocal updates
current = updates
updates = []
# 处理批量更新...
def schedule_update(item):
updates.append(item)
if len(updates) == 1: # 首次添加时调度
loop.add_callback(process_batch)
六、方法选择决策树
-
需要立即执行回调?
-
是 →
add_callback() -
否 → 需要延迟? →
call_later()
需要精确时间? →call_at()
-
-
需要监控文件描述符?
-
是 →
add_handler()+update_handler()+remove_handler()
-
-
需要执行阻塞操作?
-
是 →
run_in_executor()
-
-
需要启动事件循环?
-
主线程 →
start()
测试代码 →run_sync()
-
七、现代替代方案(Tornado 6.0+)
# 使用asyncio兼容模式
async def main():
app = Application()
app.listen(8888)
await asyncio.Event().wait() # 永久运行
if __name__ == "__main__":
asyncio.run(main())
迁移建议:
-
新项目直接使用
asyncio -
旧项目逐步替换
IOLoop为原生协程
通过合理运用这些方法,可以构建出高性能且易于维护的Tornado应用。关键是根据场景选择最匹配的API,并遵循资源管理和异常处理的最佳实践。

浙公网安备 33010602011771号