Khóa học thực hành chuyên sâu "Lập trình đồng thời thực chiến trong Python: Làm chủ Threading, Multiprocessing và Asyncio" được thiết kế theo lộ trình 4 tuần (tương ứng 4 Module). Phương pháp tiếp cận đi từ bài toán kỹ thuật gốc, mổ xẻ cơ chế điều phối, đến việc áp dụng trực tiếp vào pipeline Computer Vision, dữ liệu và Web backend thời gian thực.
| Tuần / Module | Chủ đề trọng tâm | Bản chất kỹ thuật & Điểm nghẽn | Dự án thực hành (Capstone) |
| Module 1 | Threading & Đồng bộ hóa dữ liệu | GIL, OS Context Switch, Race Condition, Deadlock, GIL-release trong C-extensions | Trình thu nạp đa luồng (Multi-threaded RTSP Streamer) không trễ, chống tràn buffer |
| Module 2 | Asyncio & Tác vụ nghẽn I/O (I/O-Bound) | Epoll/Kqueue, Event Loop internals, Future/Task lifecycles, Cooperative Multitasking | Hệ thống Webhook/Alerting bất đồng bộ đẩy log và ảnh vi phạm đa kênh đồng thời |
| Module 3 | Cầu nối Threading, Multiprocessing & Asyncio | run_in_executor, call_soon_threadsafe, IPC Queue, CPU-bound vs I/O-bound |
Pipeline lai (Hybrid Engine): Thread đọc cam $\rightarrow$ Process chạy YOLO $\rightarrow$ Asyncio stream Web |
| Module 4 | Kiến trúc hệ thống hoàn chỉnh (Production MVP) | Backpressure, Graceful Shutdown, Connection Pooling, WebSocket Broadcast | Hệ thống giám sát giao thông/sản phẩm thời gian thực (FastAPI + YOLO + SQLite/Redis) |
Mục tiêu: Hiểu rõ cơ chế cấp phát tài nguyên của OS Thread, ranh giới tác động của GIL và các kỹ thuật đồng bộ hóa dữ liệu.
Buổi 1: Cơ chế vận hành của Thread & Giới hạn của GIL (Global Interpreter Lock)
Sự khác biệt giữa OS Thread và Python Thread Object; chi phí Context Switch.
Vì sao code tính toán thuần Python chạy đa luồng không tăng tốc (thậm chí chậm hơn)?
Tại sao các hàm C-extension như cv2.VideoCapture.read(), cv2.resize(), numpy lại giải phóng GIL (Release GIL) và chạy song song hiệu quả trên Thread?
Buổi 2: Race Condition & Công cụ khóa (Synchronization Primitives)
Hiện tượng tranh chấp dữ liệu khi nhiều luồng cùng ghi vào một mảng NumPy hoặc biến đếm counter.
Ứng dụng thực tế: threading.Lock, RLock (Khóa tái nhập), Semaphore (Giới hạn tài nguyên).
Hiểm họa Deadlock và nguyên tắc cấp phát thứ tự khóa (Lock Ordering).
Buổi 3: Giao tiếp giữa các luồng với queue.Queue
Mô hình kinh điển: Producer - Consumer.
Cơ chế chặn an toàn: queue.put(), queue.get(), queue.task_done(), queue.join().
Thực hành Lab 1:
Xây dựng module ThreadedVideoReader có cơ chế tự động hủy khung hình cũ khi hàng đợi bị đầy (Drop-frame policy) để giữ độ trễ luôn ở mức micro-giây.
Mục tiêu: Làm chủ lập trình hướng sự kiện, tối ưu hóa triệt để năng lực phục vụ hàng nghìn kết nối I/O mà không tốn tài nguyên bộ nhớ.
Buổi 4: Vòng đời Coroutine, Task, Future và Event Loop
Bản chất máy trạng thái (State Machine) khi chuyển từ Generator/Yield sang Async/Await.
Cơ chế đánh thức bằng ngắt phần cứng (epoll/kqueue) thông qua module selectors.
Cách khởi tạo, hủy bỏ (task.cancel()) và xử lý ngoại lệ asyncio.CancelledError.
Buổi 5: Điều phối đồng thời (Concurrent Orchestration)
So sánh cơ chế gom nhóm: asyncio.gather(), asyncio.as_completed() và asyncio.TaskGroup (Python 3.11+).
Giới hạn tần suất gọi API / Database bằng asyncio.Semaphore.
Xử lý quá hạn: asyncio.wait_for() và chiến lược dọn dẹp tài nguyên (Resource Cleanup).
Buổi 6: Cạm bẫy "Blocking the Event Loop" & Cách khắc phục
Nhận diện các thư viện làm nghẽn Event Loop: time.sleep(), requests, sqlite3 đồng bộ.
Chuyển đổi sang thư viện async tương đương: aiohttp, aiosqlite, asyncpg.
Thực hành Lab 2:
Xây dựng micro-service bất đồng bộ: Khi nhận được 1 sự kiện cắt vạch (tripwire), hệ thống tự động lưu metadata vào aiosqlite, tải ảnh bằng chứng lên Cloud Storage và gửi tin nhắn cảnh báo qua Telegram Bot API cùng một lúc.
Mục tiêu: Kết hợp nhuần nhuyễn giữa Threading, Multiprocessing và Asyncio để giải quyết đồng thời cả tác vụ nghẽn I/O và nghẽn CPU.
Buổi 7: Cầu nối hai chiều giữa Code Đồng bộ và Bất đồng bộ
Chạy hàm đồng bộ nặng trong ThreadPool từ Asyncio: loop.run_in_executor() và asyncio.to_thread().
Giao tiếp từ Thread phụ ngược về Event Loop: Phân tích cơ chế loop.call_soon_threadsafe().
Buổi 8: Vượt qua GIL với multiprocessing & ProcessPoolExecutor
Khi nào Threading thất bại? Phân tích bài toán suy luận mô hình AI nặng (Inference YOLO / Deep Learning).
Chia sẻ bộ nhớ giữa các tiến trình (Shared Memory cho khung hình ảnh lớn) để tránh chi phí tuần tự hóa (Serialization/Pickling).
Buổi 9: Hàng đợi bất đồng bộ kết hợp đa luồng (asyncio.Queue)
Kỹ thuật đẩy luồng dữ liệu liên tục (Data Streaming) từ Thread đọc camera vào asyncio.Queue để phân phối cho nhiều Client.
Thực hành Lab 3:
Xây dựng khung xử lý: Luồng 1 (Thread) kéo RTSP $\rightarrow$ Luồng 2 (Process) chạy model YOLO $\rightarrow$ Luồng 3 (Asyncio Event Loop) nhận tọa độ bounding box để đẩy ra WebSocket.
Mục tiêu: Lắp ráp toàn bộ kiến thức vào một ứng dụng hoàn chỉnh đạt tiêu chuẩn vận hành thực tế.
Kiến trúc dự án cần triển khai:
Tầng Ingestion (Thread): Kết nối camera/video, decode khung hình vào buffer liên tục.
Tầng Processing (Thread/Process): Chạy YOLOv8 phát hiện vật thể, chạy Tracker gán ID, tính toán vector cắt vạch ảo (Virtual Tripwire).
Tầng Backend API (FastAPI + Asyncio):
Cung cấp endpoint RESTful API tra cứu dữ liệu.
Cung cấp WebSocket endpoint truyền frame đã vẽ HUD và tọa độ bounding box lên client theo thời gian thực.
Sử dụng Background Task bất đồng bộ để ghi nhận lịch sử vào cơ sở dữ liệu mà không làm giảm tốc độ khung hình.
Tầng Quản trị Vòng đời (Robustness & Lifecycle):
Cơ chế bắt tín hiệu SIGINT / SIGTERM để thực hiện Graceful Shutdown (dừng an toàn các Thread, chờ dọn dẹp hàng đợi, đóng database connections).
Dưới đây là 3 thử thách mã nguồn tương ứng với 3 mức độ để bạn bắt đầu thực hành ngay:
Yêu cầu: Tạo 10 Threads cùng tăng một biến đếm toàn cục lên 100,000 lần. Đo lường kết quả khi không dùng Lock (để thấy race condition làm sai lệch kết quả) và khi dùng Lock (kết quả chuẩn xác 1,000,000).
Yêu cầu: Viết một hàm async def download_image(url, sem) nhận vào danh sách 50 URL ảnh giả lập. Dùng asyncio.Semaphore(5) để đảm bảo tại bất kỳ thời điểm nào cũng chỉ có tối đa 5 tác vụ tải ảnh được chạy song song.
Yêu cầu: Viết một chương trình trong đó một Thread ngầm phát sinh số ngẫu nhiên mỗi 0.5 giây. Sử dụng asyncio.Queue và loop.call_soon_threadsafe() để đưa các con số này vào một Coroutine tiêu thụ đang chạy trong Event Loop chính.