asyncio.create_task()只接受协程对象,必须在运行中的事件循环内调用;传入普通函数会报TypeError,未await的协程或同步函数需先包装;任务创建后需显式await或保持主协程运行。

asyncio.create_task() 为什么不能直接传普通函数
因为 asyncio.create_task() 只接受协程对象(coroutine object),不是任意可调用对象。你写 def func(): ... 或 lambda: ...,它返回的是函数对象,不是协程——扔进去会报 TypeError: a coroutine was expected, got <function ...></function>。
常见错误现象:把同步函数或未 await 的协程(比如只写了 my_coro() 没加括号调用)传给 create_task();或者误以为装饰器(如 @sync_to_async)能自动转成协程对象,其实它返回的仍是包装后的可调用对象,不是协程。
- 确认你传进去的是「调用后立刻返回协程对象」的表达式,典型就是
my_coro()(注意括号) - 别传
my_coro(没括号)、lambda: my_coro()、functools.partial(my_coro) - 如果函数是同步的,必须先用
asyncio.to_thread()(Python 3.9+)或loop.run_in_executor()包一层,再传给create_task()
在哪个上下文里调用 create_task() 才有效
必须在已运行的事件循环内调用,否则会报 RuntimeError: no running event loop。也就是说,不能在模块顶层、普通函数体里、或同步主函数中直接调用——哪怕你写了 async def,但没用 asyncio.run() 或手动启动循环,它就只是个函数定义。
使用场景很明确:只应在 async 函数内部、且该 async 函数本身已被事件循环调度执行时调用。
立即学习“Python免费学习笔记(深入)”;
- ✅ 正确位置:
async def main(): task = asyncio.create_task(some_coro()),然后用asyncio.run(main()) - ❌ 错误位置:脚本最外层、
if __name__ == "__main__":块里直接写asyncio.create_task(...) - ⚠️ 特殊情况:如果你用的是 Jupyter(IPython 8.0+),它默认启用了异步事件循环,此时顶层调用可能“看起来”能跑,但行为不可靠,不建议依赖
create_task() 和 ensure_future() 有啥实际区别
对绝大多数日常使用来说,没区别。两者都把协程包装成 Task 对象并调度,但语义和参数支持略有不同。
ensure_future() 更底层,支持传入 Future、Task、协程、甚至实现了 __await__ 的对象;而 create_task() 是它的简化封装,只接受协程对象,且强制绑定当前事件循环(避免跨循环误用)。
- 优先用
create_task():更安全,类型检查友好,PyCharm / mypy 能更好推导 - 只有当你需要包装一个已存在的
Future(比如来自loop.create_future())时,才用ensure_future() - 别用
asyncio.Task(coroutine)手动构造——它绕过事件循环检查,容易出错,文档明确不推荐
任务没运行?可能是忘了 await 或没 hold 住事件循环
调用 create_task() 只是把任务提交进队列,不代表它会立刻执行完。如果你在 main() 里创建了任务,但没 await 它、也没 await asyncio.sleep(0) 让出控制权,或者没等它完成就退出,那任务很可能被取消或直接丢弃。
典型错误现象:打印日志没输出、HTTP 请求没发出、文件没写入——不是任务没建,是根本没轮到它跑。
- 确保你在创建任务后,至少做了其中一项:
await task、await asyncio.gather(task)、await asyncio.wait([task]) - 如果想让任务后台运行,也要保证主协程不提前结束,例如用
await asyncio.Event().wait()卡住,或用asyncio.run()包住整个逻辑 - 任务被取消时会抛
CancelledError,但默认静默——如果要调试,可以加try/except CancelledError:或启用asyncio.get_event_loop().set_debug(True)
最容易被忽略的一点:任务对象本身是强引用,但如果创建它的协程结束了,又没其他变量引用该任务,它可能被垃圾回收——尤其在短生命周期的 async 函数里,记得把 task 存到列表或类属性里,除非你明确希望它被自动清理。










