Python - Đồng bộ hóa luồng
Xin chào các nhà phép thuật Python tương lai! Hôm nay, chúng ta sẽ bắt đầu hành trình thú vị vào thế giới đồng bộ hóa luồng. Hãy tưởng tượng bạn đang ghi dấu cho một orquesta mà mỗi nhạc sĩ đều là một luồng, và bạn cần đảm bảo họ all đòi hát cùng nhau một cách hài hòa. Đó chính là điều gì đồng bộ hóa luồng trong lập trình!
Đồng bộ hóa luồng bằng các khóa (Locks)
Hãy bắt đầu với công cụ cơ bản nhất trong bộ công cụ đồng bộ hóa của chúng ta: các khóa (locks). Hãy tưởng tượng một khóa như là một dấu "không làm phiền" trên cửa phòng khách sạn. Khi một luồng có được một khóa, nó như là đặt dấu đó lên, nói với các luồng khác, "Hey, tôi đang bận!"
Dưới đây là một ví dụ đơn giản minh họa khái niệm này:
import threading
import time
# Tài nguyên chia sẻ
counter = 0
# Tạo một khóa
lock = threading.Lock()
def tang():
global counter
for _ in range(100000):
lock.acquire()
counter += 1
lock.release()
# Tạo hai luồng
thread1 = threading.Thread(target=tang)
thread2 = threading.Thread(target=tang)
# Bắt đầu các luồng
thread1.start()
thread2.start()
# Đợi các luồng kết thúc
thread1.join()
thread2.join()
print(f"Giá trị counter cuối cùng: {counter}")
Trong ví dụ này, chúng ta có một tài nguyên chia sẻ counter
mà hai luồng đang cố gắng tăng. Nếu không có khóa, chúng ta có thể gặp phải tình trạng cuộc đua, nơi cả hai luồng đều cố gắng tăng biến đếm cùng lúc, dẫn đến kết quả sai.
Bằng cách sử dụng lock.acquire()
trước khi thay đổi biến đếm và lock.release()
sau, chúng ta đảm bảo rằng chỉ một luồng có thể tăng biến đếm một lúc. Nó như là truyền cây cầu trong một cuộc đua đua nối - chỉ luồng giữ cây cầu (khóa) mới có thể chạy (thay đổi tài nguyên chia sẻ).
Các đối tượng Điều kiện để Đồng bộ hóa các Luồng Python
Bây giờ, hãy nâng cấp trò chơi đồng bộ hóa của chúng ta với các đối tượng Điều kiện. Những đối tượng này như những đèn giao thông phức tạp cho các luồng của chúng ta, cho phép sự đồng bộ hóa phức tạp hơn.
Dưới đây là một ví dụ về tình huống sản xuất - tiêu thụ sử dụng một đối tượng Điều kiện:
import threading
import time
import random
# Bộ đệm chia sẻ
buffer = []
MAX_SIZE = 5
# Tạo một đối tượng điều kiện
condition = threading.Condition()
def san_xuat():
global buffer
while True:
with condition:
while len(buffer) == MAX_SIZE:
print("Buffer đầy, sản xuất đang chờ...")
condition.wait()
item = random.randint(1, 100)
buffer.append(item)
print(f"Đã sản xuất: {item}")
condition.notify()
time.sleep(random.random())
def tieu_thu():
global buffer
while True:
with condition:
while len(buffer) == 0:
print("Buffer trống, tiêu thụ đang chờ...")
condition.wait()
item = buffer.pop(0)
print(f"Đã tiêu thụ: {item}")
condition.notify()
time.sleep(random.random())
# Tạo các luồng sản xuất và tiêu thụ
producer_thread = threading.Thread(target=san_xuat)
consumer_thread = threading.Thread(target=tieu_thu)
# Bắt đầu các luồng
producer_thread.start()
consumer_thread.start()
# Để nó chạy một lúc
time.sleep(10)
Trong ví dụ này, chúng ta có một sản xuất thêm các mục vào bộ đệm và một tiêu thụ loại bỏ các mục khỏi nó. Đối tượng Điều kiện giúp đồng bộ hóa hành động của chúng:
- Sản xuất chờ khi bộ đệm đầy.
- Tiêu thụ chờ khi bộ đệm trống.
- Họ thông báo cho nhau khi an toàn để tiếp tục.
Nó giống như một bước nhảy được kịch bản tốt, với Đối tượng Điều kiện là nhà kịch bản!
Phương thức join() để Đồng bộ hóa các luồng
Phương thức join()
như là việc bảo một luồng chờ một luồng khác hoàn thành biểu diễn của mình trước khi lên sân. Đây là cách đơn giản nhưng mạnh mẽ để đồng bộ hóa các luồng.
Dưới đây là một ví dụ:
import threading
import time
def cong_viec(ten, delay):
print(f"{ten} bắt đầu...")
time.sleep(delay)
print(f"{ten} đã hoàn thành!")
# Tạo các luồng
thread1 = threading.Thread(target=cong_viec, args=("Luồng 1", 2))
thread2 = threading.Thread(target=cong_viec, args=("Luồng 2", 4))
# Bắt đầu các luồng
thread1.start()
thread2.start()
# Đợi luồng 1 hoàn thành
thread1.join()
print("Luồng chính đang chờ sau luồng 1")
# Đợi luồng 2 hoàn thành
thread2.join()
print("Luồng chính đang chờ sau luồng 2")
print("Tất cả các luồng đã hoàn thành!")
Trong ví dụ này, luồng chính khởi tạo hai luồng công việc và sau đó chờ mỗi luồng hoàn thành bằng cách sử dụng join()
. Nó như là một bậc cha chờ con của họ hoàn thành bài tập về nhà trước khi phục bữa ăn!
Các khái niệm Đồng bộ hóa Phụ
Python cung cấp một số công cụ khác cho việc đồng bộ hóa luồng. Hãy nhanh chóng xem qua một số của chúng:
Khái niệm | Mô tả | Câu hỏi sử dụng |
---|---|---|
Semaphore | Cho phép số lượng hạn chế các luồng truy cập một tài nguyên | Quản lý một bộ kết nối cơ sở dữ liệu |
Event | Cho phép một luồng thông báo một sự kiện cho các luồng khác | Thông báo rằng một nhiệm vụ đã hoàn thành |
Barrier | Cho phép nhiều luồng chờ đến khi tất cả đạt được một điểm nhất định | Đồng bộ hóa bắt đầu của một cuộc đua |
Dưới đây là một ví dụ nhanh sử dụng một Semaphore:
import threading
import time
# Tạo một semaphore cho phép 2 luồng cùng lúc
semaphore = threading.Semaphore(2)
def cong_viec(ten):
with semaphore:
print(f"{ten} đã có được semaphore")
time.sleep(1)
print(f"{ten} đã thả semaphore")
# Tạo và bắt đầu 5 luồng
threads = []
for i in range(5):
thread = threading.Thread(target=cong_viec, args=(f"Luồng {i}",))
threads.append(thread)
thread.start()
# Đợi tất cả các luồng hoàn thành
for thread in threads:
thread.join()
print("Tất cả các luồng đã hoàn thành!")
Trong ví dụ này, semaphore hành động như một bảo vệ tại một câu lạc bộ, chỉ cho phép hai luồng vào cùng một lúc. Nó hoàn hảo cho các tình huống bạn cần giới hạn truy cập vào một tài nguyên khan hiếm!
Và thế là, các bạn! Chúng ta đã khám phá thế giới thú vị của đồng bộ hóa luồng trong Python. Hãy nhớ, như khi ghi dấu cho một orquesta hoặc kịch bản múa, việc đồng bộ hóa luồng đều liên quan đến sự đồng bộ hóa và thời gian. Với những công cụ này trong bộ công cụ lập trình của bạn, bạn đã sẵn sàng tạo ra các chương trình Python đa luồng hài hòa. Hãy tiếp tục tập luyện, giữ được sự tò mò và hạnh phúc lập trình!
Credits: Image by storyset