Xin chào các bạn, trong bài viết trước, chúng ta đã cùng khám phá về Global Interpreter Lock (GIL) và những hạn chế của nó trong ngôn ngữ Python. GIL giúp quản lý bộ nhớ dễ dàng, nhưng cũng ảnh hưởng đến hiệu suất của các chương trình, đặc biệt trong các tác vụ tính toán dựa trên CPU.
1. Khái Niệm Lập Trình Tuần Tự và Lập Trình Đồng Thời
Trước khi đi sâu vào các phương pháp lập trình đồng thời trong Python, chúng ta cần hiểu rõ khái niệm về lập trình tuần tự và lập trình đồng thời.
-
Lập Trình Tuần Tự (Sequential Programming): Tác vụ được thực hiện lần lượt, từng cái một. Chương trình chỉ chuyển sang tác vụ tiếp theo khi tác vụ hiện tại hoàn thành.
-
Lập Trình Đồng Thời (Concurrency Programming): Các tác vụ có thể được thực hiện gần như đồng thời, tức là có thể xen kẽ nhau nhưng không nhất thiết phải chạy chính xác cùng lúc. Điều này cho phép chương trình thực thi nhiều tác vụ, trong đó mỗi tác vụ có thể tạm dừng để chờ dữ liệu hoặc sự kiện.
2. Các Phương Pháp Lập Trình Đồng Thời Trong Python
Python có nhiều phương pháp để hiện thực hóa lập trình đồng thời, thậm chí khi phải đối mặt với những hạn chế của GIL. Chúng ta hãy khám phá ba phương pháp phổ biến dưới đây.
2.1 Sử Dụng Threading
Threading cho phép chúng ta chạy các luồng trong cùng một tiến trình. Đây là kỹ thuật rất hữu ích cho các tác vụ I/O-bound, như đọc/ghi tệp hoặc làm việc với dữ liệu từ mạng.
- Lưu ý: GIL làm cho chỉ một luồng có thể thực thi tại một thời điểm trong các tác vụ CPU-bound. Tuy nhiên, khi một luồng đang chờ phản hồi từ I/O, GIL sẽ được giải phóng cho phép các luồng khác thực thi, tối ưu hóa quá trình thực thi.
Trong ví dụ dưới đây, nhiều luồng thực hiện tải nội dung, mỗi luồng chỉ giữ GIL trong thời gian cần thiết để bắt đầu tải xuống.
2.2 Sử Dụng Multiprocessing
Khi thực hiện đa xử lý, mỗi tiến trình sẽ có GIL riêng của nó và không gian bộ nhớ riêng biệt, giúp vượt qua những hạn chế mà GIL gây ra. Phương pháp này lý tưởng cho các tác vụ CPU-bound.
- Sử Dụng Multi-processing: Phương pháp này giúp chúng ta tận dụng tối đa năng lực của đa lõi CPU, mặc dù việc khởi tạo nhiều tiến trình có thể tiêu tốn tài nguyên.
Một trường hợp thực tế là sử dụng Gunicorn khi triển khai ứng dụng web Python với nhiều worker process.
2.3 Sử Dụng Asyncio
Asyncio được giới thiệu trong Python 3.4 cho phép lập trình bất đồng bộ (non-blocking) thông qua cú pháp async/await
, tương tự như JavaScript. Phương pháp này rất phù hợp cho các tác vụ I/O-bound như yêu cầu mạng mà không cần sử dụng đến luồng hay các quy trình.
- Vòng Lặp Sự Kiện: Asyncio hoạt động dựa trên một vòng lặp sự kiện, cho phép các tác vụ bất đồng bộ hoạt động mà không bị chặn. Điều này làm tăng hiệu suất cho các ứng dụng như web scraping hoặc các ứng dụng web hiệu suất cao như FastAPI.
Tóm Tắt
Qua bài viết này, chúng ta đã khám phá ba phương pháp chính để thực hiện lập trình đồng thời trong Python: threading, multiprocessing, và asyncio. Mỗi phương pháp đều có ưu, nhược điểm riêng và được ứng dụng tùy thuộc vào loại tác vụ. Cá nhân mình đánh giá cao lập trình bất đồng bộ với asyncio, và bài viết tiếp theo sẽ tập trung vào chủ đề này. Cảm ơn các bạn đã theo dõi!
Mình là Phan, một nhà phát triển đầy lòng nhiệt huyết. Hẹn gặp lại các bạn trong những bài viết tiếp theo!
source: viblo