Python - Giao tiếp Giữa Các Luồng

Xin chào, các nhà phép 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 giao tiếp giữa các luồng trong Python. Đừng lo nếu bạn mới bắt đầu học lập trình – tôi sẽ là người hướng dẫn bạn, giải thích mọi thứ từng bước. Hãy bắt đầu nào!

Python - Inter-thread Communication

Giao tiếp Giữa Các Luồng là Gì?

Trước khi bước vào chi tiết, hãy hiểu rõ về giao tiếp giữa các luồng là gì. Hãy tưởng tượng bạn đang làm việc trong một nhóm để hoàn thành một dự án lớn. Mọi người đều làm việc trên các phần khác nhau, nhưng đôi khi bạn cần chia sẻ thông tin hoặc tích cực hợp tác. Đó chính xác là những gì các luồng trong một chương trình làm, và giao tiếp giữa các luồng là cách họ "nói" với nhau.

Đối Tượng Sự Kiện

Hãy bắt đầu với một trong những cách đơn giản nhất để các luồng có thể giao tiếp: đối tượng Sự kiện.

Đối tượng Sự kiện là Gì?

Đối tượng Sự kiện giống như một cờ có thể được đặt hoặc xóa. Các luồng có thể chờ đợi cờ này được đặt trước khi tiếp tục. Đó có phải như chờ đợi đèn xanh để qua đường phố.

Cách Sử Dụng Đối Tượng Sự kiện

Hãy xem một ví dụ đơn giản:

import threading
import time

def waiter(event):
print("Waiter: Tôi đang chờ sự kiện được đặt...")
event.wait()
print("Waiter: Sự kiện đã được đặt! Tôi có thể tiếp tục bây giờ.")

def setter(event):
print("Setter: Tôi sẽ đặt sự kiện sau 3 giây...")
time.sleep(3)
event.set()
print("Setter: Tôi đã đặt sự kiện!")

# Tạo một đối tượng Sự kiện
e = threading.Event()

# Tạo và bắt đầu các luồng
t1 = threading.Thread(target=waiter, args=(e,))
t2 = threading.Thread(target=setter, args=(e,))

t1.start()
t2.start()

# Chờ các luồng hoàn thành
t1.join()
t2.join()

print("Luồng chính: Đã hoàn thành!")

Hãy phân tích:

  1. Chúng ta nhập threading module để tạo luồng và time module để thêm độ trễ.
  2. Chúng ta định nghĩa hai hàm: waitersetter.
  3. Hàm waiter chờ đợi sự kiện được đặt bằng cách sử dụng event.wait().
  4. Hàm setter chờ đợi 3 giây (mô tả một số công việc) và sau đó đặt sự kiện bằng cách sử dụng event.set().
  5. Chúng ta tạo một đối tượng Sự kiện e.
  6. Chúng ta tạo hai luồng, một cho mỗi hàm, truyền đối tượng Sự kiện cho cả hai.
  7. Chúng ta bắt đầu cả hai luồng và sau đó sử dụng join() để chờ chúng hoàn thành.

Khi bạn chạy đoạn mã này, bạn sẽ thấy waiter đang chờ, sau 3 giây, setter đặt sự kiện, và waiter tiếp tục.

Đối Tượng Điều Kiện

Bây giờ, hãy leo một tầng và xem đối tượng Điều kiện. Nó giống như chúng của đối tượng Sự kiện thân thiện hơn.

Đối tượng Điều kiện là Gì?

Đối tượng Điều kiện cho phép các luồng chờ đợi các điều kiện cụ thể trở thành đúng. Nó giống như chờ đợi một người cụ thể đến cuộc hòa nhạc trước khi bắt đầu các trò chơi.

Cách Sử Dụng Đối Tượng Điều kiện

Dưới đây là một ví dụ sử dụng Đối tượng Điều kiện:

import threading
import time
import random

# Tài nguyên chia sẻ
items = []
condition = threading.Condition()

def producer():
global items
for i in range(5):
time.sleep(random.random())  # Mô tả các thời gian sản xuất khác nhau
with condition:
items.append(f"Sản phẩm {i}")
print(f"Nhà sản xuất đã thêm Sản phẩm {i}")
condition.notify()  # Thông báo cho người tiêu dùng rằng có sản phẩm có sẵn

def consumer():
global items
while True:
with condition:
while not items:
print("Người tiêu dùng đang chờ...")
condition.wait()
item = items.pop(0)
print(f"Người tiêu dùng đã loại bỏ {item}")
time.sleep(random.random())  # Mô tả các thời gian tiêu thụ khác nhau

# Tạo và bắt đầu các luồng
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

# Chờ nhà sản xuất hoàn thành
producer_thread.join()

# Dừng người tiêu dùng (nó đang ở vòng lặp vô hạn)
consumer_thread.daemon = True

Hãy phân tích:

  1. Chúng ta tạo một tài nguyên chia sẻ items và một Đối tượng Điều kiện.
  2. Hàm producer thêm các sản phẩm vào danh sách và thông báo cho người tiêu dùng.
  3. Hàm consumer chờ đợi các sản phẩm có sẵn, sau đó loại bỏ và "tiêu thụ" chúng.
  4. Chúng ta sử dụng with condition: để tự động nhận và nhả khoá của điều kiện.
  5. condition.wait() thả khoá và chờ đợi thông báo.
  6. condition.notify() đánh thức một luồng đang chờ.

Ví dụ này minh họa một tình huống sản xuất - tiêu thụ cổ điển, nơi một luồng sản xuất sản phẩm và một luồng tiêu thụ chúng.

So Sánh Giữa Đối Tượng Sự kiện và Đối Tượng Điều kiện

Dưới đây là so sánh nhanh giữa Đối tượng Sự kiện và Đối tượng Điều kiện:

Tính Năng Sự kiện Điều kiện
Mục Đích Đơn giản thông báo Đồng bộ hóa phức tạp
Trạng thái Nhị phân (đặt/xóa) Có thể có nhiều trạng thái
Chờ đợi Các luồng chờ đợi sự kiện được đặt Các luồng chờ đợi các điều kiện cụ thể
Thông báo Tất cả các luồng đang chờ đợi được thông báo Có thể thông báo một hoặc tất cả các luồng đang chờ đợi
Cách sử dụng Các tình huống "đi/halte" đơn giản Các vấn đề sản xuất - tiêu thụ, đồng bộ hóa phức tạp

Kết Luận

Xin chúc mừng! Bạn đã bước ra đầu tiên vào thế giới giao tiếp giữa các luồng trong Python. Chúng ta đã bao gồm Đối tượng Sự kiện cho thông báo đơn giản và Đối tượng Điều kiện cho các tình huống đồng bộ hóa phức tạp hơn.

Hãy nhớ, như học bất kỳ ngôn ngữ mới nào, luyện tập sẽ làm bạn hoàn hảo. Thử viết các chương trình của riêng bạn sử dụng các đối tượng này. Có lẽ tạo một hệ thống chat đơn giản nơi các luồng đại diện cho các người dùng khác nhau, hoặc mô tả hệ thống đèn giao thông sử dụng các sự kiện.

Giao tiếp giữa các luồng có thể có vẻ khó khăn ban đầu, nhưng với thời gian và luyện tập, bạn sẽ điều khiển các luồng như một nhà dẫn nhạc triệu tài. Hãy tiếp tục mã hóa, tiếp tục học hỏi, và above all, have fun!

Credits: Image by storyset