Python bài 23: Thread, start, join và concurrency
- 18-10-2026
- Toanngo92
- 0 Comments
Giáo trình Python miễn phí · Chương 12/12: Đa luồng và đồng bộ · Bài 23/24.
Mục lục
Mục tiêu bài học
Chạy các công việc chờ đợi đồng thời, hiểu vòng đời thread và giới hạn với tác vụ tính toán.
Concurrency khác parallelism
Concurrency tổ chức nhiều công việc có tiến độ xen kẽ; parallelism là thực thi thực sự cùng lúc. Thread là một luồng thực thi trong cùng tiến trình, thường chia sẻ bộ nhớ với các thread khác. Nhờ đó trao đổi dữ liệu thuận tiện nhưng có nguy cơ tranh chấp.
Thread thường hữu ích khi chờ I/O như mạng hoặc tệp. Với CPython ở chế độ có GIL, nhiều thread không đồng nghĩa tăng tốc tính toán Python thuần trên nhiều lõi. Có bản dựng free-threaded tùy phiên bản và cấu hình; không nên coi mọi Python runtime có cùng giới hạn hoặc kết luận bỏ GIL sẽ tự làm mã thread-safe.
Tạo và chờ thread
Thread nhận callable qua target và đối số qua args/kwargs. start() khởi chạy trong thread mới; gọi run() trực tiếp chỉ thực hiện như lời gọi hàm trong thread hiện tại. join() chờ thread kết thúc; không tự chạy thread hoặc trả giá trị return của target.
from queue import Queue
from threading import Thread
from time import sleep
ket_qua = Queue()
def xu_ly(ten, delay):
sleep(delay)
ket_qua.put(f"Hoàn thành {ten}")
threads = [
Thread(target=xu_ly, args=("A", 0.02)),
Thread(target=xu_ly, args=("B", 0.01)),
]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
messages = [ket_qua.get() for _ in threads]
for message in sorted(messages):
print(message)
Kết quả:
Hoàn thành A Hoàn thành B
Thứ tự không được đảm bảo
Thread B có thời gian chờ ngắn hơn có thể hoàn thành trước, nhưng không dùng điều đó làm quy tắc nghiệp vụ. Ví dụ sắp xếp trước khi in để đầu ra ổn định. Trong ứng dụng, gắn id công việc vào kết quả để biết dữ liệu thuộc yêu cầu nào.
Thread chỉ start một lần; muốn làm lại, tạo đối tượng mới. join(timeout=…) giới hạn thời gian chờ của bên gọi, không dừng thread. Kiểm tra is_alive() nếu cần biết nó còn hoạt động. Không có thao tác chung an toàn để cưỡng bức kết thúc mọi thread tùy ý.
Điểm dừng và lỗi
Công việc lâu có thể dùng threading.Event để yêu cầu dừng hợp tác; worker tự kiểm tra ở vị trí thích hợp. Ngoại lệ trong thread không tự truyền lại chỗ join(); cần đưa lỗi vào Queue hoặc dùng Future. Không dùng daemon thread cho công việc quan trọng rồi mong nó luôn hoàn tất khi chương trình chính thoát.
Lỗi thường gặp và lưu ý
- Không dùng sleep() làm cơ chế đồng bộ chính; thời gian chờ tùy tiện không bảo đảm dữ liệu sẵn sàng.
- Không tạo số lượng thread không giới hạn cho danh sách URL; giới hạn worker và timeout từng tác vụ.
Ghi nhớ
Thread cần quản lý vòng đời, điểm dừng, kết quả và lỗi. Tiếp theo là bảo vệ dữ liệu dùng chung bằng Lock và Queue.
