Điều khiển thread bằng cooperative event
Python không cung cấp API chính thống để dừng một thread đang chạy giữa chừng — không có cái tương đương pthread_cancel của POSIX hay Thread.stop() được khuyến cáo. Lý do là an toàn dữ liệu: nếu raise exception bất kỳ chỗ nào trong thread, lock có thể bị giữ vĩnh viễn, file descriptor mở dở, hoặc invariant của object bị phá. Cách “chính thống” duy nhất để báo hiệu dừng là dùng pattern cooperative: thread đích chủ động kiểm tra một flag dùng chung và tự thoát khi flag được set.
Mẫu hình của xthread
xthread là thư viện cài đặt pattern này thành một wrapper quanh threading.Thread. Hai khả năng pause/unpause và stop non-preemptive đều dựa trên threading.Event:
__is_runninglà event điều khiển vòng đời tổng.stop()clear event, vòng lặp wrapper thoát.__resumelà event điều khiển pause.pause()clear event,unpause()set lại. Wrapper gọi__resume.wait(pause_timeout)sau mỗi lần chạy target, nên khi pause, thread block ở wait cho tới khi unpause hoặc timeout.
Target callable được gọi định kỳ trong vòng while __is_running.is_set(): thay vì chạy một lần duy nhất. Đây là điểm cốt lõi của pattern: hợp đồng giữa thư viện và người dùng là target phải hoàn thành trong một burst ngắn, để wrapper có cơ hội kiểm tra event giữa các lần gọi.
def wrapper():
while self.__is_running.is_set():
try:
result = target(self, *args, **kwargs)
self.__resume.wait(self.__pause_timeout)
except Exception as e:
self.__on_error(e)
Hệ quả: pattern này không thể dừng một target đang sleep dài hoặc đang chờ một syscall blocking — chừng nào target chưa return, wrapper không thể kiểm tra event.
So với các cách “thật sự” preemptive
CPython có hàm PyThreadState_SetAsyncExc ở tầng C-API, cho phép raise một exception trong một thread khác tại điểm boundary giữa các opcode. Wrapper Python của nó là internal, không được expose chính thức, và Python doc cảnh báo rõ là “không an toàn nếu thread đích đang giữ lock”. POSIX có pthread_cancel kèm cancellation point, an toàn hơn nhưng yêu cầu code phải có cleanup handler đúng cách.
So với hai cách trên, cooperative event đổi quyền điều khiển lấy an toàn: thread đích quyết định khi nào nó “an toàn để dừng” thay vì bị ép. Triết lý này gần với coroutine và event loop: tất cả đều dựa trên cộng tác thay vì preemption.
Khi nào pattern này phù hợp
Pattern xthread phù hợp với các thread “polling” hoặc “periodic worker”: kiểm tra hàng đợi, đọc sensor, tick game loop — nơi target tự nhiên chạy thành burst ngắn lặp đi lặp lại. Nó không thay thế được async cho I/O-bound (asyncio cho hiệu năng tốt hơn) và không phù hợp khi target là một tác vụ tuyến tính dài.
Trải nghiệm cá nhân
Pattern này được kết tinh khi viết xthread tại Teko (7/2023) — dự án practice để giải bài toán nhiều dev dùng threading.Thread sai cách trong team SRE. Bài học sau cùng đọng lại: API gọn quan trọng hơn flexibility. Chi tiết bối cảnh và bài học trong transcript phỏng vấn.
Nguồn tham khảo
- Source xthread - implementation
- threading - Thread-based parallelism | Python documentation
- Python C API - PyThreadState_SetAsyncExc
- Why Python’s Thread.stop was deprecated
Liên kết tri thức
- Global Interpreter Lock trong Python - nền tảng giải thích vì sao raise exception giữa thread không an toàn
- Coroutine trong Python - cùng triết lý cộng tác, đơn vị tự nhường quyền điều khiển thay vì bị ép
- Event loop trong Python - event loop là cooperative scheduler cấp cao hơn của cùng triết lý này
- Đơn giản hơn linh hoạt trong thiết kế API - xthread thừa tuỳ chọn so với nhu cầu thực, là nguồn rút ra bài học này
- Dựng lại để hiểu sâu - xthread là một ví dụ của dựng lại API threading để internalize cooperative concurrency
- Game loop cơ bản - game loop là cooperative scheduler cho frame, cùng triết lý “không block, hỏi định kỳ”
- Time-based movement thay vì sleep trong game loop - cùng nguyên lý cooperative áp dụng cho entity trong game
- Cấp resource ở đâu, giải phóng ở đó - cùng triết lý “owner chủ động quyết định, không bị ép từ ngoài”
Tags
Cập nhật: 2026-05-29