1. Giới thiệu về lập trình đa luồng

Khái niệm đa luồng

Lập trình đa luồng (multithreading) cho phép thực hiện nhiều luồng (thread) đồng thời trong một ứng dụng. Mỗi luồng có thể chạy các tác vụ khác nhau, giúp tận dụng tốt hơn tài nguyên hệ thống.

Lợi ích của lập trình đa luồng

  • Hiệu suất cao hơn: Thực hiện đồng thời nhiều tác vụ giúp tiết kiệm thời gian.
  • Tối ưu hóa I/O: Đặc biệt hữu ích trong các tác vụ nhập/xuất, như đọc và ghi tệp hoặc kết nối mạng.
  • Phản hồi tức thì: Cải thiện trải nghiệm người dùng bằng cách duy trì giao diện người dùng (UI) mượt mà trong khi xử lý các tác vụ nặng.

2. Thư viện threading

Cài đặt và import thư viện

Python đã tích hợp sẵn thư viện threading, do đó bạn chỉ cần import để sử dụng:

import threading

Các thành phần chính trong thư viện threading

  • Thread: Lớp dùng để tạo và quản lý luồng.
  • Lock: Cơ chế để đồng bộ hóa giữa các luồng.
  • Event: Cơ chế để giao tiếp giữa các luồng.

3. Tạo và chạy luồng

Khởi tạo một luồng đơn giản

Để tạo một luồng, bạn chỉ cần khởi tạo một đối tượng Thread và cung cấp hàm sẽ thực thi:

import threading
import time

def print_numbers():
    for i in range(5):
        time.sleep(1)
        print(i)

# Tạo luồng
thread = threading.Thread(target=print_numbers)

# Khởi chạy luồng
thread.start()

# Chờ luồng hoàn thành
thread.join()

print("Luồng đã hoàn thành!")

Chờ luồng hoàn thành

Sử dụng phương thức join() để chờ luồng hoàn thành trước khi tiếp tục thực hiện các lệnh khác.

4. Sử dụng nhiều luồng

Tạo và quản lý nhiều luồng

Để chạy nhiều luồng, bạn có thể tạo nhiều đối tượng Thread và khởi chạy chúng như sau:

def print_even_numbers():
    for i in range(0, 10, 2):
        time.sleep(1)
        print(i)

def print_odd_numbers():
    for i in range(1, 10, 2):
        time.sleep(1)
        print(i)

# Tạo luồng
even_thread = threading.Thread(target=print_even_numbers)
odd_thread = threading.Thread(target=print_odd_numbers)

# Khởi chạy luồng
even_thread.start()
odd_thread.start()

# Chờ cả hai luồng hoàn thành
even_thread.join()
odd_thread.join()

print("Cả hai luồng đã hoàn thành!")

5. Đồng bộ hóa luồng

Vấn đề đua (race condition)

Khi nhiều luồng truy cập vào cùng một tài nguyên, có thể xảy ra tình trạng đua, dẫn đến kết quả không chính xác.

Sử dụng Lock để đồng bộ hóa

Sử dụng Lock để đảm bảo rằng chỉ một luồng có thể truy cập vào tài nguyên tại một thời điểm.

lock = threading.Lock()
counter = 0

def increment():
    global counter
    for _ in range(100000):
        lock.acquire()
        counter += 1
        lock.release()

# Tạo các luồng
threads = [threading.Thread(target=increment) for _ in range(2)]

# Khởi chạy các luồng
for thread in threads:
    thread.start()

# Chờ các luồng hoàn thành
for thread in threads:
    thread.join()

print(f"Giá trị cuối cùng của counter: {counter}")

6. Sử dụng ThreadPoolExecutor

Giới thiệu về ThreadPoolExecutor

ThreadPoolExecutor cho phép quản lý nhóm các luồng, giúp tối ưu hóa việc sử dụng tài nguyên và đơn giản hóa quá trình quản lý luồng.

Tạo và sử dụng ThreadPoolExecutor

Dưới đây là cách sử dụng ThreadPoolExecutor để thực hiện các tác vụ đồng thời:

from concurrent.futures import ThreadPoolExecutor
import time

def task(n):
    time.sleep(1)
    return n * n

# Sử dụng ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(task, range(10)))

print("Kết quả:", results)

7. Kết luận

Tóm tắt các khái niệm chính

Lập trình đa luồng trong Python cho phép thực hiện nhiều tác vụ đồng thời, cải thiện hiệu suất và trải nghiệm người dùng. Thư viện threading cung cấp các công cụ cần thiết để tạo và quản lý các luồng, cũng như các cơ chế đồng bộ hóa để tránh tình trạng đua.

Hướng đi tiếp theo trong lập trình đa luồng

Để phát triển kỹ năng lập trình đa luồng, hãy thực hành với các dự án thực tế và tìm hiểu thêm về các chủ đề nâng cao như lập trình bất đồng bộ (asynchronous programming) và các mô hình đồng bộ hóa phức tạp hơn.