Python - Mảng Thread

Xin chào, các bạn nhà lập trình Python đầy kỳ niệm! Hôm nay, chúng ta sẽ bơi lội vào thế giới thú vị của Mảng Thread. Là một giáo viên máy tính hàng xóm bạn thân thiện, tôi đến đây để hướng dẫn bạn qua cuộc hành trình này, bước nhảy bước đi. Đừng lo nếu bạn mới bắt đầu với lập trình; chúng ta sẽ bắt đầu từ những khái niệm cơ bản và làm việc lên từ đó. Vậy hãy lấy ly đồ uống yêu thích của bạn, thư giãn và hãy bắt đầu cuộc phiêu lưu của chúng ta!

Python - Thread Pools

Mảng Thread là gì?

Trước khi chúng ta nhảy vào mã, hãy hiểu rõ về mảng thread là gì và tại sao chúng lại quan trọng. Hãy tưởng tượng bạn đang quản lý một nhà hàng buồn rộn. Thay vì thuê nhân viên mới mỗi khi có khách vào, bạn có một đội ngũ phục vụ sẵn sàng phục vụ. Đội ngũ đó là "mảng" của bạn các công nhân. Trong lập trình, một mảng thread tương tự - đó là một nhóm các thread có thể tái sử dụng sẵn sàng làm việc khi cần.

Mảng thread giúp chúng ta quản lý nhiều nhiệm vụ một cách hiệu quả mà không cần phải tạo ra các thread mới cho mỗi nhiệm vụ. Chúng rất hữu ích khi bạn có nhiều nhiệm vụ ngắn sống cần được thực hiện đồng thời.

Bây giờ, hãy khám phá hai cách chính để triển khai mảng thread trong Python: lớp ThreadPool và lớp ThreadPoolExecutor.

Sử dụng lớp Python ThreadPool

Lớp ThreadPool là một phần của mô-đun multiprocessing.pool. Nó có chút cũ kỹ nhưng vẫn được sử dụng rộng rãi. Hãy xem cách chúng ta có thể sử dụng nó:

from multiprocessing.pool import ThreadPool
import time

def worker(num):
print(f"Worker {num} đang bắt đầu")
time.sleep(2)  # Mô phỏng một chút công việc
print(f"Worker {num} đã hoàn thành")
return num * 2

# Tạo một mảng thread với 3 thread làm việc
pool = ThreadPool(3)

# Gửi 5 nhiệm vụ vào mảng
results = pool.map(worker, range(5))

# Đóng mảng và chờ tất cả các nhiệm vụ hoàn thành
pool.close()
pool.join()

print("Tất cả các công nhân đã hoàn thành")
print(f"Kết quả: {results}")

Hãy phân tích điều này:

  1. Chúng ta nhập ThreadPooltime (cho công việc mô phỏng của chúng ta).
  2. Chúng ta định nghĩa hàm worker mô phỏng một chút công việc và trả về một giá trị.
  3. Chúng ta tạo một ThreadPool với 3 thread làm việc.
  4. Chúng ta sử dụng pool.map() để gửi 5 nhiệm vụ vào mảng. Điều này phân phối các nhiệm vụ giữa các thread có sẵn.
  5. Chúng ta đóng mảng và chờ tất cả các nhiệm vụ hoàn thành.
  6. Cuối cùng, chúng ta in các kết quả.

Khi bạn chạy điều này, bạn sẽ thấy rằng mặc dù chúng ta có 5 nhiệm vụ, nhưng chúng được thực hiện bởi 3 thread làm việc, minh họa cách mảng thread quản lý công việc.

Sử dụng lớp Python ThreadPoolExecutor

Bây giờ, hãy xem xét lớp ThreadPoolExecutor hiện đại hơn từ mô-đun concurrent.futures. Lớp này cung cấp giao diện cao hơn để thực hiện các callable không đồng bộ.

from concurrent.futures import ThreadPoolExecutor
import time

def worker(num):
print(f"Worker {num} đang bắt đầu")
time.sleep(2)  # Mô phỏng một chút công việc
print(f"Worker {num} đã hoàn thành")
return num * 2

# Tạo một ThreadPoolExecutor với 3 thread làm việc
with ThreadPoolExecutor(max_workers=3) as executor:
# Gửi 5 nhiệm vụ vào executor
futures = [executor.submit(worker, i) for i in range(5)]

# Chờ tất cả các nhiệm vụ hoàn thành và nhận kết quả
results = [future.result() for future in futures]

print("Tất cả các công nhân đã hoàn thành")
print(f"Kết quả: {results}")

Hãy phân tích điều này:

  1. Chúng ta nhập ThreadPoolExecutor thay vì ThreadPool.
  2. Chúng ta sử dụng câu lệnh with để tạo và quản lý executor. Điều này đảm bảo rằng sẽ có sự dọn dẹp đúng khi chúng ta hoàn thành.
  3. Chúng ta sử dụng executor.submit() để gửi các nhiệm vụ cá nhân vào mảng.
  4. Chúng ta tạo một danh sách các đối tượng Future, đại diện cho kết quả cuối cùng của các nhiệm vụ của chúng ta.
  5. Chúng ta sử dụng future.result() để chờ và nhận kết quả của từng nhiệm vụ.

ThreadPoolExecutor cung cấp sự linh hoạt hơn và thường dễ dàng sử dụng hơn, đặc biệt là trong các tình huống phức tạp hơn.

So sánh ThreadPool và ThreadPoolExecutor

Hãy so sánh hai phương pháp này:

Tính năng ThreadPool ThreadPoolExecutor
Mô-đun multiprocessing.pool concurrent.futures
Phiên bản Python Tất cả các phiên bản 3.2 và sau
Quản lý ngữ cảnh Không
Linh hoạt ít Nhiều
Xử lý ngoại lệ Cơ bản Nâng cao
Hủy bỏ Hạn chế Được hỗ trợ
Đối tượng Future Không

Như bạn thấy, ThreadPoolExecutor cung cấp nhiều tính năng hơn và thường linh hoạt hơn. Tuy nhiên, ThreadPool vẫn hữu ích, đặc biệt nếu bạn đang làm việc với các phiên bản Python cũ hơn hoặc cần duy trì tính tương thích với mã hiện có.

Các thực hành tốt và mẹo

  1. Chọn số lượng thread đúng: Quá ít thread có thể không sử dụng đầy đủ CPU, trong khi quá nhiều có thể gây ra tải phần cứng. Một điểm bắt đầu tốt là số lượng lõi CPU trên máy của bạn.

  2. Sử dụng quản lý ngữ cảnh: Với ThreadPoolExecutor, luôn sử dụng câu lệnh with để đảm bảo sự dọn dẹp đúng.

  3. Xử lý ngoại lệ: Đảm bảo rằng bạn xử lý ngoại lệ trong các hàm công nhân để tránh các lỗi靜寂.

  4. Chú ý đến tài nguyên chia sẻ: Khi sử dụng mảng thread, cẩn thận với các tài nguyên chia sẻ để tránh các tình huống cạnh tranh.

  5. Xem xét độ nhỏ nhất của nhiệm vụ: Mảng thread hoạt động tốt nhất với nhiều nhiệm vụ nhỏ hơn là thay vì một số nhiệm vụ lớn.

Kết luận

Xin chúc mừng! Bạn đã bước ra đầu tiên vào thế giới của mảng thread trong Python. Chúng ta đã đi qua các khái niệm cơ bản của cả ThreadPoolThreadPoolExecutor, và bạn nên có một nền tảng tốt để bắt đầu sử dụng các công cụ mạnh mẽ này trong các dự án của riêng bạn.

Nhớ rằng, như học nấu ăn trong một nhà hàng buồn rộn, việc thành thạo mảng thread cần thực hành. Đừng sợ thử nghiệm và gặp lỗi - đó là cách chúng ta học! Tiếp tục lập trình, tiếp tục học hỏi, và trước khi bạn biết, bạn sẽ nắm vững việc quản lý thread như một đầu bếp chuyên nghiệp trong một nhà hàng buồn rộn.

Chúc mãi mãi mãi có các thread của bạn luôn hòa hợp!

Credits: Image by storyset