Contents

Cách nâng cao mã Python của bạn bằng tính đồng thời và song song

Bài học chính

Tính đồng thời và tính song song thể hiện các nguyên lý thiết yếu của hiệu suất tác vụ tính toán, mỗi tính năng sở hữu các thuộc tính duy nhất giúp phân biệt chúng với nhau.

Tính đồng thời cho phép phân bổ tài nguyên hiệu quả và tăng khả năng phản hồi trong các ứng dụng, trong khi tính song song đóng vai trò quan trọng trong việc đạt được hiệu suất cao nhất và khả năng mở rộng quy mô.

Python cung cấp nhiều cách tiếp cận khác nhau để quản lý các hoạt động đồng thời, bao gồm việc sử dụng các luồng thông qua thư viện luồng tích hợp, cũng như hỗ trợ lập trình không đồng bộ bằng khung asyncio. Ngoài ra, mô-đun đa xử lý cho phép các nhà phát triển khai thác sức mạnh của xử lý song song trong ứng dụng của họ.

Đồng thời đề cập đến khả năng hệ thống thực thi nhiều tiến trình hoặc luồng cùng một lúc, trong khi song song là khả năng chia một nhiệm vụ thành các nhiệm vụ phụ nhỏ hơn và thực hiện chúng đồng thời bởi các phần khác nhau của hệ thống. Trong Python, tồn tại nhiều cách tiếp cận khác nhau để quản lý tính đồng thời và song song, chẳng hạn như đa xử lý, phân luồng, lập trình không đồng bộ với async/await và sử dụng các thư viện như Celery hoặc Dask cho điện toán phân tán. Tuy nhiên, những lựa chọn này có thể dẫn đến nhầm lẫn khi quyết định cách tiếp cận phù hợp nhất cho một tình huống cụ thể.

Đi sâu vào mảng tài nguyên và khung có thể tạo điều kiện thuận lợi một cách hiệu quả cho việc triển khai các kỹ thuật lập trình đồng thời trong Python, cũng như sự khác biệt giữa chúng với nhau.

Hiểu về tính đồng thời và song song

Đồng thời và song song là hai khái niệm quan trọng mô tả cách thực hiện các tác vụ trong hệ thống máy tính, mỗi tác vụ có các thuộc tính riêng.

⭐ Đồng thời là khả năng của một chương trình quản lý nhiều tác vụ cùng một lúc mà không nhất thiết phải thực hiện chúng cùng một lúc. Nó xoay quanh ý tưởng xen kẽ các nhiệm vụ, chuyển đổi giữa chúng theo cách có vẻ đồng thời. /vi/images/task-executed-currently.jpg

⭐ Mặt khác, tính song song liên quan đến việc thực hiện nhiều nhiệm vụ một cách thực sự song song. Nó thường tận dụng lợi thế của nhiều lõi CPU hoặc bộ xử lý. Tính song song đạt được khả năng thực thi đồng thời thực sự, cho phép bạn thực hiện các tác vụ nhanh hơn và rất phù hợp cho các hoạt động tính toán chuyên sâu. /vi/images/task-executed-in-parallel.jpg

Tầm quan trọng của tính đồng thời và tính song song

Tầm quan trọng của việc xử lý đồng thời và song song trong điện toán là không thể chối cãi, vì nó cho phép thực hiện nhiều tác vụ đồng thời, nhờ đó tăng hiệu quả và giảm thời gian thực hiện tổng thể. Cách tiếp cận này ngày càng trở nên quan trọng do nhu cầu ngày càng tăng về khả năng xử lý nhanh hơn và hiệu quả hơn trên nhiều ứng dụng, từ mô phỏng khoa học đến tự động hóa quy trình làm việc kinh doanh. Bằng cách tận dụng sức mạnh của bộ xử lý đa lõi và hệ thống phân tán, xử lý đồng thời và song song cho phép cải thiện việc sử dụng tài nguyên và nâng cao khả năng mở rộng, cuối cùng dẫn đến hiệu suất tốt hơn và tăng năng suất.

Có thể đạt được sự phân bổ tài nguyên được tối ưu hóa thông qua tính đồng thời vì nó cho phép khai thác hiệu quả tài sản hệ thống, đảm bảo rằng các quy trình tiếp tục phát triển một cách hiệu quả thay vì thụ động chờ đợi các tài nguyên bên ngoài.

Khả năng nâng cao khả năng phản hồi của ứng dụng, đặc biệt liên quan đến giao diện người dùng và tương tác với máy chủ web, là một lợi thế đáng chú ý liên quan đến tính đồng thời.

Hiệu suất nâng cao có thể đạt được thông qua tính song song, đặc biệt là trong các tác vụ tính toán phụ thuộc nhiều vào các đơn vị xử lý trung tâm (CPU) như tính toán phức tạp, thao tác dữ liệu và mô phỏng mô hình.

Khả năng mở rộng là một khía cạnh quan trọng của thiết kế hệ thống, đòi hỏi cả thực thi đồng thời và xử lý song song để đạt được hiệu suất tối ưu ở quy mô lớn. Khả năng xử lý khối lượng công việc ngày càng tăng trong khi vẫn duy trì hiệu quả là điều tối quan trọng trong phát triển phần mềm hiện đại.

Trước các xu hướng mới nổi trong công nghệ phần cứng ưu tiên khả năng xử lý đa lõi, các hệ thống phần mềm cần tận dụng hiệu quả tính song song để đảm bảo khả năng tồn tại và bền vững lâu dài của chúng.

Đồng thời trong Python

Việc thực thi đồng thời có thể đạt được bằng Python thông qua việc sử dụng các kỹ thuật phân luồng hoặc không đồng bộ, được hỗ trợ bởi thư viện asyncio.

Luồng trong Python

Phân luồng là một tính năng nội tại trong lập trình Python cho phép tạo và quản lý nhiều tác vụ đồng thời trong một quy trình thống nhất. Cơ chế này tỏ ra đặc biệt thuận lợi đối với các tác vụ có thao tác đầu vào/đầu ra nặng hoặc những tác vụ có thể thu lợi từ việc thực thi song song.

Mô-đun phân luồng của Python cung cấp giao diện cấp cao để tạo và quản lý các luồng. Mặc dù GIL (Khóa phiên dịch toàn cầu) giới hạn các luồng về tính song song thực sự, nhưng chúng vẫn có thể đạt được tính tương tranh bằng cách xen kẽ các tác vụ một cách hiệu quả.

Mã được cung cấp thể hiện một phiên bản lập trình đồng thời thông qua việc sử dụng các luồng trong Python. Cụ thể, nó sử dụng thư viện Yêu cầu Python để bắt đầu yêu cầu HTTP, đây là một hoạt động điển hình liên quan đến các hoạt động đầu vào-đầu ra (I/O) và có thể dẫn đến chặn các tác vụ. Ngoài ra, mã tận dụng mô-đun thời gian để xác định thời lượng thực hiện chương trình.

 import requests
import time
import threading

urls = [
    'https://www.google.com',
    'https://www.wikipedia.org',
    'https://www.makeuseof.com',
]

# function to request a URL
def download_url(url):
    response = requests.get(url)
    print(f"Downloaded {url}-Status Code: {response.status_code}")


# Execute without threads and measure execution time
start_time = time.time()

for url in urls:
    download_url(url)

end_time = time.time()
print(f"Sequential download took {end_time-start_time:.2f} seconds\n")


# Execute with threads, resetting the time to measure new execution time
start_time = time.time()
threads = []

for url in urls:
    thread = threading.Thread(target=download_url, args=(url,))
    thread.start()
    threads.append(thread)

# Wait for all threads to complete
for thread in threads:
    thread.join()

end_time = time.time()
print(f"Threaded download took {end_time-start_time:.2f} seconds")

Thật vậy, việc thực thi chương trình này cho thấy sự nâng cao rõ rệt về hiệu quả khi sử dụng các luồng đồng thời để thực hiện các hoạt động đòi hỏi nhiều I/O, bất chấp sự chênh lệch nhỏ về thời gian giữa các lần thực thi tuần tự và song song.

/vi/images/executing-tasks-sequentially-and-in-threads.jpg

Lập trình không đồng bộ với Asyncio

asyncio cung cấp một vòng lặp sự kiện quản lý các tác vụ không đồng bộ được gọi là coroutineÂs. Coroutine là các hàm mà bạn có thể tạm dừng và tiếp tục, khiến chúng trở nên lý tưởng cho các tác vụ liên quan đến I/O. Thư viện này đặc biệt hữu ích cho các tình huống trong đó các tác vụ liên quan đến việc chờ đợi các tài nguyên bên ngoài, chẳng hạn như các yêu cầu mạng.

Để điều chỉnh ví dụ gửi yêu cầu đồng bộ trước đó để sử dụng với lập trình không đồng bộ trong Python, bạn cần thực hiện một số thay đổi. Đầu tiên, thay vì sử dụng request.get()time.sleep() , vốn đang chặn các hoạt động tạm dừng thực thi cho đến khi hoàn thành hoặc hết thời gian, bạn nên sử dụng các lựa chọn thay thế không chặn như asyncioaiohttp. Điều này liên quan đến việc gói mã hiện có trong một hàm không đồng bộ bằng cách sử dụng async def và thay thế các hoạt động I/O truyền thống bằng các hoạt động không đồng bộ của chúng. Ví dụ: async with aiohttp.ClientSession().post(url) có thể được sử dụng để gửi yêu cầu POST không đồng bộ mà không cần chờ phản hồi. Ngoài ra, việc xử lý và ghi nhật ký lỗi cũng có thể yêu cầu điều chỉnh để phù hợp với khung không đồng bộ mới.

 import asyncio
import aiohttp
import time

urls = [
    'https://www.google.com',
    'https://www.wikipedia.org',
    'https://www.makeuseof.com',
]

# asynchronous function to request URL
async def download_url(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            content = await response.text()
            print(f"Downloaded {url}-Status Code: {response.status}")

# Main asynchronous function
async def main():
    # Create a list of tasks to download each URL concurrently
    tasks = [download_url(url) for url in urls]

    # Gather and execute the tasks concurrently
    await asyncio.gather(*tasks)

start_time = time.time()

# Run the main asynchronous function
asyncio.run(main())

end_time = time.time()

print(f"Asyncio download took {end_time-start_time:.2f} seconds")

Bằng cách sử dụng mã được cung cấp, người ta có thể thực hiện hiệu quả nhiều lượt tải xuống trang web đồng thời bằng cách tận dụng các khả năng của asyncio và khai thác sức mạnh của các hoạt động I/O không đồng bộ. Trái ngược với các kỹ thuật phân luồng truyền thống phù hợp hơn với các tác vụ sử dụng nhiều CPU, phương pháp này đặc biệt hiệu quả trong việc tối ưu hóa các quy trình liên quan đến I/O.

/vi/images/use-asyncio-to-execute-task-concurrently.jpg

Tính song song trong Python

Bạn có thể triển khai tính song song bằng cách sử dụng mô-đun đa xử lý của Python, cho phép bạn tận dụng tối đa lợi thế của bộ xử lý đa lõi.

Đa xử lý trong Python

Mô-đun đa xử lý của Python cung cấp một cách tiếp cận để khai thác tính song song thông qua việc tạo ra các quy trình riêng lẻ, mỗi quy trình được trang bị miền bộ nhớ và trình thông dịch Python riêng biệt. Bằng cách đó, phương pháp này sẽ phá vỡ Khóa phiên dịch toàn cầu (GIL) thường thấy trong các tác vụ liên quan đến CPU.

 import requests
import multiprocessing
import time

urls = [
    'https://www.google.com',
    'https://www.wikipedia.org',
    'https://www.makeuseof.com',
]

# function to request a URL
def download_url(url):
    response = requests.get(url)
    print(f"Downloaded {url}-Status Code: {response.status_code}")

def main():
    # Create a multiprocessing pool with a specified number of processes
    num_processes = len(urls)
    pool = multiprocessing.Pool(processes=num_processes)

    start_time = time.time()
    pool.map(download_url, urls)
    end_time = time.time()

    # Close the pool and wait for all processes to finish
    pool.close()
    pool.join()

    print(f"Multiprocessing download took {end_time-start_time:.2f} seconds")

main()

Đa xử lý cho phép thực thi đồng thời chức năng download\_url trên nhiều quy trình, từ đó tạo điều kiện thuận lợi cho việc xử lý song song URL đã cho.

/vi/images/execute-task-in-parallel.jpg

Khi nào nên sử dụng đồng thời hoặc song song

Quyết định lựa chọn xử lý đồng thời hoặc xử lý song song phụ thuộc vào đặc điểm của các hoạt động đang được thực hiện và khả năng tài nguyên phần cứng của hệ thống cơ bản.

Khi xử lý các hoạt động tập trung vào đầu vào/đầu ra (I/O), như thao tác đọc/ghi tệp hoặc yêu cầu kết nối mạng, bạn nên sử dụng đồng thời do khả năng xử lý nhiều tác vụ cùng một lúc. Ngoài ra, nó có thể được sử dụng trong các tình huống mà hạn chế về bộ nhớ đặt ra thách thức.

Khi sử dụng đa xử lý là phù hợp cho các hoạt động sử dụng nhiều CPU có thể được tăng cường bằng cách thực thi đồng thời và khi việc đảm bảo phân tách mạnh mẽ các quy trình là ưu tiên hàng đầu, vì lỗi của mỗi quy trình sẽ không ảnh hưởng quá mức đến các quy trình khác.

Tận dụng tính đồng thời và song song

Song song và thực thi đồng thời là các phương pháp khả thi để nâng cao hiệu quả và thông lượng của các chương trình Python. Tuy nhiên, điều quan trọng là phải hiểu được sự khác biệt giữa các kỹ thuật này để đưa ra quyết định sáng suốt về cách tiếp cận nào phù hợp nhất với một tình huống cụ thể.

Python cung cấp một bộ công cụ và mô-đun toàn diện cho phép các nhà phát triển nâng cao hiệu quả mã của họ bằng cách tận dụng các kỹ thuật xử lý đồng thời hoặc song song, bất kể các tác vụ trước mắt chủ yếu là tính toán hay có tính chất đầu vào/đầu ra chuyên sâu.