Đ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:

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.

Cập nhật: 2026-05-29