Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.
Cách sử dụng Semaphore trong Python
Trong lập trình đa luồng, việc kiểm soát truy cập vào tài nguyên chia sẻ là một thách thức lớn. Nếu nhiều luồng cùng truy cập vào tài nguyên này một cách đồng thời mà không có cơ chế quản lý, sẽ dễ dẫn đến các vấn đề về đồng bộ hóa như điều kiện cạnh tranh (race conditions) và xung đột dữ liệu. Để giải quyết vấn đề này, Python cung cấp Semaphore - một công cụ đồng bộ hóa mạnh mẽ cho phép bạn giới hạn số lượng luồng có thể truy cập vào tài nguyên chia sẻ cùng lúc. Trong bài viết này, bạn sẽ học cách sử dụng Semaphore trong Python để đảm bảo rằng chỉ một số lượng nhất định các luồng có thể truy cập tài nguyên chia sẻ, giúp ngăn ngừa các vấn đề về đồng bộ hóa và đảm bảo hoạt động ổn định của chương trình.

Giới thiệu về Semaphore trong Python
Semaphore trong Python là một công cụ đồng bộ hóa cho phép bạn kiểm soát việc truy cập vào một tài nguyên chia sẻ. Cơ bản, một semaphore là một bộ đếm liên kết với một khóa, giới hạn số lượng luồng có thể truy cập vào tài nguyên chia sẻ cùng lúc.
Semaphore giúp ngăn chặn các vấn đề đồng bộ hóa luồng như điều kiện cạnh tranh (race conditions), khi nhiều luồng cố gắng truy cập tài nguyên cùng lúc và làm nhiễu loạn hoạt động của nhau.
Semaphore duy trì một bộ đếm. Khi một luồng muốn truy cập tài nguyên chia sẻ, semaphore kiểm tra bộ đếm.
Bài viết này được đăng tại [free tuts .net]
Nếu bộ đếm lớn hơn không, nó giảm bộ đếm và cho phép luồng truy cập tài nguyên. Nếu bộ đếm bằng không, semaphore sẽ chặn luồng cho đến khi bộ đếm lớn hơn không.
Semaphore có hai thao tác chính:
- Acquire: Thao tác acquire kiểm tra bộ đếm và giảm nó nếu lớn hơn không. Nếu bộ đếm bằng không, semaphore sẽ chặn luồng cho đến khi một luồng khác giải phóng semaphore.
- Release: Thao tác release tăng bộ đếm, cho phép các luồng khác có thể acquire nó.
Sử dụng Semaphore trong Python
Để sử dụng semaphore, bạn làm theo các bước sau:
-
Đầu tiên, import module threading:
import threading
Thứ hai, tạo một đối tượng Semaphore và chỉ định số lượng luồng có thể acquire nó cùng lúc:
semaphore = threading.Semaphore(3)
-
Trong ví dụ này, chúng ta tạo một đối tượng Semaphore chỉ cho phép tối đa ba luồng acquire nó cùng lúc.
-
Thứ ba, acquire semaphore từ một luồng bằng cách gọi phương thức
acquire():
semaphore.acquire()
-
Nếu bộ đếm semaphore bằng không, luồng sẽ đợi cho đến khi một luồng khác giải phóng semaphore. Sau khi acquire semaphore, bạn có thể thực thi một đoạn mã quan trọng.
-
Cuối cùng, giải phóng semaphore sau khi chạy đoạn mã quan trọng bằng cách gọi phương thức
release():
semaphore.release()
Để đảm bảo semaphore được acquire và release một cách chính xác, ngay cả khi xảy ra ngoại lệ trong khi chạy đoạn mã quan trọng, bạn có thể sử dụng câu lệnh with:
with semaphore:
# Code trong khối này đã acquire semaphore
# Thực hiện các thao tác trên tài nguyên chia sẻ
# ...
# Semaphore được giải phóng bên ngoài khối with
Câu lệnh with sẽ acquire và release semaphore tự động, làm cho mã của bạn ít dễ bị lỗi hơn.
Ví dụ về Semaphore trong Python
Ví dụ sau đây minh họa cách sử dụng semaphore để giới hạn số lượng tải xuống đồng thời tối đa là ba bằng cách sử dụng multithreading trong Python:
import threading
import urllib.request
MAX_CONCURRENT_DOWNLOADS = 3
semaphore = threading.Semaphore(MAX_CONCURRENT_DOWNLOADS)
def download(url):
with semaphore:
print(f"Đang tải xuống {url}...")
response = urllib.request.urlopen(url)
data = response.read()
print(f"Đã tải xong {url}")
return data
def main():
# Các URL để tải xuống
urls = [
'https://www.ietf.org/rfc/rfc791.txt',
'https://www.ietf.org/rfc/rfc792.txt',
'https://www.ietf.org/rfc/rfc793.txt',
'https://www.ietf.org/rfc/rfc794.txt',
'https://www.ietf.org/rfc/rfc795.txt',
]
# Tạo các luồng cho mỗi lượt tải xuống
threads = []
for url in urls:
thread = threading.Thread(target=download, args=(url,))
threads.append(thread)
thread.start()
# Đợi tất cả các luồng hoàn thành
for thread in threads:
thread.join()
if __name__ == '__main__':
main()
Kết quả
Đang tải xuống https://www.ietf.org/rfc/rfc791.txt... Đang tải xuống https://www.ietf.org/rfc/rfc792.txt... Đang tải xuống https://www.ietf.org/rfc/rfc793.txt... Đã tải xong https://www.ietf.org/rfc/rfc792.txt Đang tải xuống https://www.ietf.org/rfc/rfc794.txt... Đã tải xong https://www.ietf.org/rfc/rfc791.txt Đang tải xuống https://www.ietf.org/rfc/rfc795.txt... Đã tải xong https://www.ietf.org/rfc/rfc793.txt Đã tải xong https://www.ietf.org/rfc/rfc794.txt Đã tải xong https://www.ietf.org/rfc/rfc795.txt
Giải thích chi tiết
Đầu tiên, import module threading và urllib.request:
import threading import urllib.request
Thứ hai, tạo một đối tượng Semaphore để kiểm soát số lượng luồng có thể tải xuống đồng thời tối đa là ba:
MAX_CONCURRENT_DOWNLOADS = 3 semaphore = threading.Semaphore(MAX_CONCURRENT_DOWNLOADS)
Thứ ba, định nghĩa hàm download() để tải xuống từ một URL. Hàm download acquire và release semaphore bằng cách sử dụng câu lệnh with. Nó cũng sử dụng module urllib.request để tải dữ liệu từ một URL:
def download(url):
with semaphore:
print(f"Đang tải xuống {url}...")
response = urllib.request.urlopen(url)
data = response.read()
print(f"Đã tải xong {url}")
return data
Thứ tư, định nghĩa hàm main() để tạo năm luồng dựa trên danh sách URL và bắt đầu chúng để tải dữ liệu:
def main():
# Các URL để tải xuống
urls = [
'https://www.ietf.org/rfc/rfc791.txt',
'https://www.ietf.org/rfc/rfc792.txt',
'https://www.ietf.org/rfc/rfc793.txt',
'https://www.ietf.org/rfc/rfc794.txt',
'https://www.ietf.org/rfc/rfc795.txt',
]
# Tạo các luồng cho mỗi lượt tải xuống
threads = []
for url in urls:
thread = threading.Thread(target=download, args=(url,))
threads.append(thread)
thread.start()
# Đợi tất cả các luồng hoàn thành
for thread in threads:
thread.join()
Cuối cùng, gọi hàm main() trong phần if __name__ == '__main__':
if __name__ == '__main__':
main()
Kết bài
Hy vọng rằng bài viết này đã giúp bạn hiểu rõ cách sử dụng semaphore trong Python để kiểm soát số lượng luồng có thể truy cập tài nguyên chia sẻ cùng lúc. Việc sử dụng phương thức acquire() để kiểm tra và giảm bộ đếm của semaphore, cũng như phương thức release() để tăng bộ đếm, là rất quan trọng để đảm bảo đồng bộ hóa. Bằng cách sử dụng câu lệnh with, bạn có thể tự động và an toàn acquire và release semaphore, giúp mã nguồn của bạn trở nên gọn gàng và ít lỗi hơn. Sử dụng semaphore một cách hiệu quả sẽ giúp bạn tránh được các vấn đề về điều kiện cạnh tranh và xung đột dữ liệu, đảm bảo hoạt động ổn định và hiệu quả cho các ứng dụng đa luồng.

Các kiểu dữ liệu trong C ( int - float - double - char ...)
Thuật toán tìm ước chung lớn nhất trong C/C++
Cấu trúc lệnh switch case trong C++ (có bài tập thực hành)
ComboBox - ListBox trong lập trình C# winforms
Random trong Python: Tạo số random ngẫu nhiên
Lệnh cin và cout trong C++
Cách khai báo biến trong PHP, các loại biến thường gặp
Download và cài đặt Vertrigo Server
Thẻ li trong HTML
Thẻ article trong HTML5
Cấu trúc HTML5: Cách tạo template HTML5 đầu tiên
Cách dùng thẻ img trong HTML và các thuộc tính của img
Thẻ a trong HTML và các thuộc tính của thẻ a thường dùng