Cách sửa lỗi 'RuntimeError: no running event loop' trong Python 3.10, 3.11 và 3.12

intermediate🐍 Python2026-07-11| Python 3.10, 3.11, 3.12 hoặc các phiên bản mới hơn trên Windows, Linux hoặc macOS.

Error Message

RuntimeError: no running event loop
#python#asyncio#event-loop#xử lý đồng thời#debugging

Tại sao mã Async của bạn đột nhiên bị lỗiViệc nâng cấp lên Python 3.10 hoặc 3.11 thường mang lại cảm giác hài lòng cho đến khi các script không đồng bộ (asynchronous) của bạn đột ngột gặp sự cố. Mã nguồn vốn chạy hoàn hảo trong nhiều năm trên Python 3.8 giờ đây có thể ném ra một thông báo lỗi khó chịu:

RuntimeError: no running event loop

Điều này xảy ra vì Python đã thay đổi cách xử lý "engine" bên dưới của mã async. Trong các phiên bản cũ, việc gọi asyncio.get_event_loop() sẽ âm thầm tạo một vòng lặp (loop) mới nếu chưa có vòng lặp nào đang chạy. Bắt đầu từ Python 3.10, các nhà phát triển đã thắt chặt các quy tắc này. Giờ đây, hàm này yêu cầu một vòng lặp phải tồn tại sẵn. Nếu không tìm thấy, nó sẽ dừng lại và ném ra ngoại lệ này.

Kịch bản thường gặpBạn có khả năng sẽ gặp phải lỗi này khi làm theo các hướng dẫn cũ hoặc bảo trì các script kế thừa (legacy). Nó thường xuyên xuất hiện khi tích hợp asyncio với các thư viện như aiohttp hoặc discord.py bên ngoài thiết lập tiêu chuẩn của chúng. Đây là mô hình kinh điển thường gây lỗi trong Python hiện đại:

import asyncio

async def my_task():
    print("Task is running")
    await asyncio.sleep(1)

# Cách này hoạt động vào năm 2019, nhưng thất bại trên Python 3.10+
loop = asyncio.get_event_loop()
loop.run_until_complete(my_task())

Logic đằng sau sự thay đổiCác nhà duy trì đã loại bỏ việc tạo vòng lặp ngầm định để ngăn chặn tình trạng rò rỉ tài nguyên lộn xộn. Trong các ứng dụng đa luồng phức tạp, việc các vòng lặp tự động khởi chạy đã tạo ra các lỗi không thể dự đoán trước, vốn là một cơn ác mộng khi gỡ lỗi. Bằng cách buộc bạn phải khai báo tường minh, Python đảm bảo mã của bạn ổn định hơn và dễ dàng mở rộng hơn. Kể từ khi phát hành Python 3.10.0 vào cuối năm 2021, đây đã trở thành tiêu chuẩn mới cho hệ sinh thái này.

Giải pháp tốt nhất: Sử dụng asyncio.run()Cách khắc phục gọn gàng nhất là ngừng tự quản lý vòng lặp. Python 3.7 đã giới thiệu asyncio.run(), vốn là tiêu chuẩn vàng trong hơn năm năm qua. Nó xử lý các tác vụ nặng nề—tạo vòng lặp, chạy mã của bạn và dọn dẹp sau đó—chỉ trong một dòng lệnh duy nhất.

Cách tiếp cận hiện đại:```

import asyncio

async def main(): print("Task is running") await asyncio.sleep(1)

if name == "main": # Đây là cách tiếp cận hiện đại và an toàn asyncio.run(main())


`asyncio.run()` rất mạnh mẽ. Nó đảm bảo một vòng lặp mới được kích hoạt trước khi các tác vụ của bạn bắt đầu và đóng nó lại một cách chính xác khi hoàn thành. Điều này giúp ngăn chặn hoàn toàn lỗi 'no running event loop'.
## Xử lý các dự án phức tạp hoặc đa luồngCác cách khắc phục tiêu chuẩn không phải lúc nào cũng đủ cho các tác vụ mạng cấp thấp hoặc quản lý luồng tùy chỉnh. Nếu kiến trúc của bạn yêu cầu một đối tượng vòng lặp thủ công, bạn phải khởi tạo nó một cách tường minh bằng cách sử dụng chính sách vòng lặp sự kiện (event loop policy).
### Khởi tạo thủ côngNếu bạn tuyệt đối cần một tham chiếu `loop` ở cấp cao nhất, hãy làm theo mô hình tường minh này:

import asyncio

async def main(): print("Executing in a manually set loop")

1. Tạo vòng lặp thủ công

loop = asyncio.new_event_loop()

2. Gán nó vào luồng hiện tại

asyncio.set_event_loop(loop)

try: loop.run_until_complete(main()) finally: # 3. Luôn đóng vòng lặp để ngăn rò rỉ bộ nhớ loop.close()


### Khắc phục lỗi trong các luồng (Threads)Các vòng lặp sự kiện là cục bộ theo luồng (thread-local). Nếu bạn gọi `asyncio.get_event_loop()` bên trong một `threading.Thread`, nó sẽ thất bại vì vòng lặp của luồng chính không hiển thị ở đó. Bạn phải cấp cho mỗi luồng một "engine" riêng:

import asyncio import threading

def thread_worker(): # Mỗi luồng cần thiết lập vòng lặp riêng new_loop = asyncio.new_event_loop() asyncio.set_event_loop(new_loop)

new_loop.run_until_complete(asyncio.sleep(1))
print("Thread task finished")
new_loop.close()

Khởi chạy 3 luồng worker

threads = [threading.Thread(target=thread_worker) for _ in range(3)] for t in threads: t.start() for t in threads: t.join()


## Còn đối với Jupyter Notebook thì sao?Jupyter và IPython là những trường hợp đặc biệt. Chúng chạy vòng lặp sự kiện riêng trong nền để xử lý các ô (cell) tương tác. Nếu bạn cố gắng sử dụng `asyncio.run()` ở đó, bạn sẽ nhận được một lỗi khác thông báo rằng một vòng lặp đang chạy.
Cách khắc phục rất đơn giản: chỉ cần `await` hàm của bạn trực tiếp trong ô đó.

Bên trong một ô Jupyter

import asyncio

async def my_task(): await asyncio.sleep(1) print("Done")

Không cần sử dụng asyncio.run()

await my_task()


## Danh sách kiểm tra xác minh nhanhĐể đảm bảo bản sửa lỗi của bạn hoạt động ổn định, hãy kiểm tra qua bốn bước sau:

  - **Xác minh phiên bản:** Xác nhận bạn đang sử dụng Python 3.10+ bằng cách chạy `python --version`.
  - **Kiểm tra điểm đầu vào:** Đảm bảo tất cả việc thực thi async bắt đầu từ một lệnh gọi `asyncio.run()` duy nhất nếu có thể.
  - **Kiểm tra dọn dẹp:** Nếu bạn đã sử dụng `new_event_loop()`, hãy xác minh rằng `loop.is_closed()` là `True` sau khi script kết thúc.
  - **Cách ly luồng:** Nếu sử dụng nhiều luồng, hãy dùng `id(asyncio.get_event_loop())` để xác nhận mỗi luồng có một ID vòng lặp duy nhất.
Bằng cách ngừng sử dụng `get_event_loop()`, bạn không chỉ đang sửa một lỗi đơn thuần. Bạn đang chuẩn bị cho mã nguồn của mình sẵn sàng cho tương lai với Python 3.13 và các phiên bản sau này.

Related Error Notes