1. Giới thiệu
Tầm quan trọng của xử lý song song trong Python
Python là một ngôn ngữ lập trình đơn giản và dễ sử dụng, được ứng dụng rộng rãi trong nhiều lĩnh vực. Tuy nhiên, khi xử lý dữ liệu phức tạp hoặc tính toán nặng, tốc độ xử lý của Python có thể trở thành một vấn đề. Để giải quyết vấn đề này, “xử lý song song” giúp thực hiện nhiều tác vụ đồng thời đóng vai trò quan trọng. Bài viết này sẽ giới thiệu cách triển khai xử lý song song trong Python, từ các phương pháp cơ bản đến các tình huống ứng dụng cụ thể.
2. Các phương pháp xử lý song song trong Python
Các phương pháp chính để xử lý song song
Trong Python, có một số phương pháp để thực hiện xử lý song song. Ba phương pháp chính là:
- Đa luồng (
threading
module)
Sử dụng nhiều luồng để thực hiện các tác vụ song song. Tuy nhiên, do ảnh hưởng của GIL (Global Interpreter Lock) trong Python, hiệu quả của phương pháp này bị hạn chế đối với các tác vụ sử dụng nhiều CPU. - Đa tiến trình (
multiprocessing
module)
Mỗi tiến trình có không gian bộ nhớ riêng biệt, do đó không bị ảnh hưởng bởi GIL và có thể thực hiện xử lý song song thực sự. Phù hợp với xử lý dữ liệu lớn hoặc tính toán nặng. - Xử lý bất đồng bộ (
asyncio
module)
Phương pháp này đặc biệt hiệu quả với các tác vụ I/O như giao tiếp mạng hoặc thao tác tệp. Nó giúp xử lý các tác vụ có thời gian chờ dài một cách hiệu quả.

3. So sánh giữa Đa tiến trình và Đa luồng
Ảnh hưởng của GIL (Global Interpreter Lock)
Python có một cơ chế gọi là GIL (Global Interpreter Lock), cơ chế này giới hạn chỉ có một luồng (thread) được thực thi tại một thời điểm. Điều này khiến cho việc tăng số lượng luồng không cải thiện hiệu suất đối với các tác vụ sử dụng nhiều CPU. Vì vậy, xử lý song song bằng luồng chỉ hiệu quả với các tác vụ I/O (đọc/ghi tệp, giao tiếp mạng, v.v.).
Ưu điểm và hạn chế của Đa luồng
Luồng (thread) là một cơ chế nhẹ và phù hợp với các tác vụ I/O (xử lý tệp, giao tiếp mạng, v.v.). Tuy nhiên, do hạn chế của GIL, nó không thể tận dụng hoàn toàn nhiều lõi CPU, vì vậy không phù hợp cho các tác vụ sử dụng nhiều tài nguyên CPU.
“`
import threading
import time
def worker(num):
print(f”Worker {num} bắt đầu”)
time.sleep(2)
print(f”Worker {num} hoàn thành”)
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
“`
Đoạn mã trên tạo và chạy 5 luồng đồng thời, mỗi luồng sẽ dừng lại 2 giây trước khi kết thúc. Điều này minh họa cách mà các luồng có thể chạy song song.
Ưu điểm của Đa tiến trình
Để tránh hạn chế của GIL, đa tiến trình là một giải pháp hiệu quả. Khác với luồng, tiến trình có không gian bộ nhớ riêng, giúp tận dụng toàn bộ sức mạnh của nhiều lõi CPU. Điều này đặc biệt hữu ích đối với các tác vụ tính toán nặng hoặc xử lý dữ liệu lớn.
“`
from multiprocessing import Process
import time
def worker(num):
print(f”Worker {num} bắt đầu”)
time.sleep(2)
print(f”Worker {num} hoàn thành”)
if __name__ == ‘__main__’:
processes = []
for i in range(5):
p = Process(target=worker, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
“`
Đoạn mã này tạo ra 5 tiến trình chạy song song. Phương thức join()
đảm bảo rằng chương trình chờ cho đến khi tất cả tiến trình kết thúc trước khi tiếp tục.
4. Cách triển khai xử lý song song trong Python
Xử lý song song bằng module multiprocessing
Module multiprocessing
cho phép quản lý nhiều tiến trình một cách hiệu quả. Dưới đây là một ví dụ về cách sử dụng Pool để xử lý danh sách các tác vụ song song.
“`
from multiprocessing import Pool
def square(x):
return x * x
if __name__ == ‘__main__’:
with Pool(4) as p:
result = p.map(square, [1, 2, 3, 4, 5])
print(result)
“`
Trong đoạn mã trên, bốn tiến trình được sử dụng để tính bình phương của các số trong danh sách. Kết quả sẽ được trả về dưới dạng danh sách.
5. Xử lý bất đồng bộ và các ứng dụng
Xử lý bất đồng bộ bằng module asyncio
Module asyncio
rất hữu ích cho các tác vụ có thời gian chờ như giao tiếp mạng hoặc thao tác tệp. Bằng cách thực thi đồng thời nhiều tác vụ trong khi chờ, chương trình có thể chạy hiệu quả hơn.
“`
import asyncio
async def worker(num):
print(f’Worker {num} bắt đầu’)
await asyncio.sleep(1)
print(f’Worker {num} hoàn thành’)
async def main():
tasks = [worker(i) for i in range(5)]
await asyncio.gather(*tasks)
asyncio.run(main())
“`
Đoạn mã này chạy 5 tác vụ bất đồng bộ song song. Lệnh await
giúp xử lý bất đồng bộ, cho phép thực hiện các tác vụ khác trong thời gian chờ.

6. Tối ưu hiệu suất xử lý song song
Tối ưu hóa bằng Joblib
Joblib là một thư viện giúp tối ưu hóa các tác vụ tính toán nặng, đặc biệt hữu ích trong xử lý dữ liệu và huấn luyện mô hình học máy. Dưới đây là một ví dụ về cách sử dụng Joblib để thực hiện xử lý song song.
“`
from joblib import Parallel, delayed
def heavy_task(n):
return n ** 2
results = Parallel(n_jobs=4)(delayed(heavy_task)(i) for i in range(10))
print(results)
“`
Trong đoạn mã trên, n_jobs
xác định số tiến trình sẽ chạy song song. Với n_jobs=4
, bốn tiến trình sẽ xử lý tính toán đồng thời.
7. Ứng dụng thực tế của xử lý song song trong Python
Xử lý dữ liệu và Web Scraping
Xử lý song song trong Python rất hữu ích trong các tác vụ như xử lý dữ liệu lớn và web scraping. Ví dụ, khi thu thập dữ liệu từ nhiều trang web, việc sử dụng đa luồng hoặc xử lý bất đồng bộ có thể giúp gửi nhiều yêu cầu đồng thời, giảm đáng kể thời gian thực thi.
Trong huấn luyện mô hình học máy hoặc tiền xử lý dữ liệu, sử dụng multiprocessing
hoặc Joblib
có thể tăng hiệu suất đáng kể.
8. Kết luận
Xử lý song song là một kỹ thuật quan trọng để tối ưu hóa hiệu suất trong Python. Việc sử dụng đúng các mô-đun như threading
, multiprocessing
, asyncio
và Joblib
sẽ giúp cải thiện hiệu suất cho nhiều loại tác vụ. Hãy áp dụng những kỹ thuật này vào dự án thực tế để đạt hiệu quả cao nhất!