cuốn sách gpt4 ai đã làm

Tóm tắt đồng thời Python: lập trình đa luồng, đa tiến trình và không đồng bộ

In lại Tác giả: Sahara Thời gian cập nhật: 2024-12-31 14:44:47 57 4
mua khóa gpt4 Nike

Với sự phát triển của lập trình đa lõi, đồng thời trong Python ngày càng phổ biến và phát triển nhanh chóng.

Một mặt, Python cung cấp nhiều công cụ lập trình đồng thời.

Ví dụ: đa luồng truyền thống có thể dễ dàng tạo và quản lý các luồng thông qua mô-đun luồng, mô-đun này có thể được sử dụng cho các tác vụ đòi hỏi nhiều I/O;

Đa tiến trình, sử dụng mô-đun đa xử lý để tận dụng tối đa CPU đa lõi, phù hợp với các tác vụ đòi hỏi nhiều CPU.

Mặt khác, với sự gia tăng của lập trình không đồng bộ. Thư viện asyncio cũng cho phép các nhà phát triển viết mã không đồng bộ hiệu quả và cải thiện hiệu suất chương trình, đặc biệt khi xử lý một số lượng lớn các kịch bản hoạt động I/O đồng thời.

Tuy nhiên, Khóa phiên dịch toàn cầu (GIL) trong Python đã mang đến những thách thức đáng kể cho việc lập trình đồng thời. Hiện tại, cộng đồng đang tích cực khám phá các phương pháp và chiến lược tối ưu hóa để vượt qua GIL nhằm thúc đẩy quá trình phát triển liên tục của lập trình đồng thời Python.

Bài viết này dự định giới thiệu lần lượt cách sử dụng các phương thức đa luồng, đa tiến trình hoặc không đồng bộ để viết chương trình trong Python.

1. Đa luồng

Mô-đun đa luồng trong Python là threading, được thêm vào thư viện chuẩn ngay từ Python 1.5.

Phân luồng đã được phát triển, đặc biệt là sau khi vào Python3.x.

Từ Python3.3 đến Python3.13, hầu hết mọi bản nâng cấp Python đều đi kèm với các thay đổi về luồng.

Do đó, khi sử dụng nó, hãy đảm bảo sử dụng giao diện luồng chính xác theo phiên bản Python của bạn.

1.1 Tình huống sử dụng và hạn chế

Tính năng đa luồng của Python được sử dụng rộng rãi trong các tình huống tác vụ đòi hỏi nhiều I/O, chẳng hạn như yêu cầu mạng, đọc và ghi tệp, v.v., cho phép chương trình chuyển sang các luồng khác trong khi chờ thao tác I/O, từ đó cải thiện hiệu quả tổng thể.

Khi các kịch bản ứng dụng mở rộng, những hạn chế của đa luồng dần dần được nêu bật.

Điều quan trọng nhất là Khóa phiên dịch toàn cầu (GIL), đây là một tính năng của trình thông dịch Python. Mỗi lần chỉ có một luồng có thể thực thi mã byte Python.

Điều này dẫn đến việc đa luồng không thể tận dụng tối đa ưu điểm của CPU đa lõi trong các tác vụ ngốn CPU và sự cải thiện hiệu năng không rõ ràng, thậm chí có thể giảm đi.

Tuy nhiên, bất chấp những hạn chế của nó, đa luồng vẫn đóng một vai trò quan trọng trong hệ sinh thái Python.

Các nhà phát triển tiếp tục khám phá các phương pháp tối ưu hóa, chẳng hạn như sử dụng luồng kết hợp với các mô-đun tương tranh khác như đa xử lý, để tối đa hóa điểm mạnh và tránh điểm yếu. Đồng thời, phiên bản Python mới cũng đang cố gắng cải thiện cơ chế GIL để cung cấp nhiều khả năng phát triển đa luồng hơn.

1.2. Cách sử dụng

Trong quá trình phát triển thực tế, có ba cách chính để sử dụng đa luồng:

Cách đầu tiên là tạo thread trực tiếp bằng lớp threading.Thread.

Đây là cách cơ bản nhất, trực tiếp khởi tạo lớp threading.Thread và truyền vào hàm đích và các tham số.

import threading def worker(): print('Thread is doing') # Tạo thread t = threading.Thread(target=worker) # Bắt đầu thread t.start() # Đợi thread hoàn thành việc thực thi t.join()

Cách thứ hai là tạo một lớp luồng bằng cách kế thừa lớp threading.Thread và ghi đè phương thức run để xác định các tác vụ được thực hiện bởi luồng.

nhập lớp luồng MyThread(threading.Thread): def run(self): print(f'{self.name} luồng đang thực thi') #Tạo phiên bản luồng my_thread = MyThread() #Start thread my_thread.start() #Wait for thread Đã hoàn thành việc thực thi my_thread.join()

Cách cuối cùng là sử dụng threading.ThreadPool để triển khai nhóm luồng. Trong Python 3, nên sử dụng ThreadPoolExecutor trong mô-đun concurrent.futures để triển khai chức năng nhóm luồng.

threading.ThreadPool đã được đánh dấu là lỗi thời và không được khuyến khích sử dụng trong các dự án mới.

Ưu điểm của nhóm luồng là nó có thể quản lý một nhóm luồng, tái sử dụng tài nguyên luồng và giảm chi phí tạo và hủy luồng.

import concurrent.futures def task(num): print(f"Execute task{num}") return num * 2 # Tạo một nhóm luồng, số lượng luồng tối đa là 3 với concurrent.futures.ThreadPoolExecutor(max_workers=3) là người thực thi: # Gửi tác vụ Future_to_num = {executor.submit(task, num): num for num in range(5)} cho tương lai trong concurrent.futures.as_completed(future_to_num): num = Future_to_num[future] try: result = Future.result() ngoại trừ Ngoại lệ là e: print(f"Task {num} không thực thi được: {e}") else: print( f "Nhiệm vụ {num} kết quả: {result}")

Kết quả thực hiện:

$ python.exe .\thread.py Thực thi nhiệm vụ 0 Thực thi nhiệm vụ 1 Thực thi nhiệm vụ 2 Thực thi nhiệm vụ 3 Kết quả nhiệm vụ 1: 2 Thực thi nhiệm vụ 4 Kết quả nhiệm vụ 2: 4 Kết quả nhiệm vụ 0: 0 Kết quả nhiệm vụ 3: 6 Kết quả nhiệm vụ 4: 8

2. Đa quy trình

Mô-đun đa xử lý đa luồng đã được giới thiệu trong Python 2.6 và tiếp tục phát triển trong Python 3.x.

Trong quá trình phát triển, đa xử lý liên tục được cải tiến. Nó cung cấp một giao diện đơn giản và mạnh mẽ, cho phép các nhà phát triển dễ dàng tạo và quản lý nhiều quy trình, tận dụng tối đa lợi thế của CPU đa lõi và cải thiện đáng kể hiệu quả xử lý các tác vụ sử dụng nhiều CPU.

Nó hỗ trợ nhiều phương thức giao tiếp giữa các quá trình, chẳng hạn như hàng đợi, đường ống, v.v., để tạo điều kiện chia sẻ và đồng bộ hóa dữ liệu giữa các quy trình.

2.1 Tình huống sử dụng và hạn chế

Đa xử lý phù hợp với tính toán sử dụng nhiều CPU, chẳng hạn như tính toán khoa học, phân tích dữ liệu, xử lý hình ảnh và các tác vụ khác đòi hỏi lượng lớn tài nguyên máy tính.

Đa xử lý cũng có thể được sử dụng khi có nhiều tác vụ độc lập cần được thực thi đồng thời, chẳng hạn như xử lý tệp hàng loạt, xử lý hàng đợi tác vụ, v.v. Một quy trình có thể được chỉ định cho từng nhiệm vụ để nâng cao hiệu quả thực hiện nhiệm vụ.

Ngoài ra, trong một số ứng dụng máy chủ, nhiều quy trình cũng có thể được sử dụng để cho phép quy trình chính xử lý các yêu cầu trong khi các quy trình khác chịu trách nhiệm về các tác vụ nền, chẳng hạn như cập nhật bộ đệm dữ liệu, ghi nhật ký, v.v., từ đó tránh chặn luồng chính và cải thiện tốc độ phản hồi của ứng dụng.

Tuy nhiên, đa xử lý cũng có một số hạn chế.

Vì mỗi tiến trình có một không gian bộ nhớ độc lập nên việc chia sẻ và liên lạc dữ liệu giữa các tiến trình tương đối phức tạp và yêu cầu các cơ chế và hoạt động đồng bộ hóa bổ sung, điều này có thể gây giảm hiệu suất.

Hơn nữa, việc tạo và hủy các tiến trình rất tốn kém và việc tạo và hủy các tiến trình thường xuyên sẽ ảnh hưởng đến hiệu suất tổng thể của chương trình.

Ngoài ra, các tình huống sử dụng của nó tương đối hạn chế và không phù hợp với các tác vụ đồng thời đơn giản. So với đa luồng, ưu điểm của nó trong các tác vụ đòi hỏi nhiều I/O là không rõ ràng, vì đa luồng có thể chuyển sang thực hiện các tác vụ khác trong khi chờ đợi. cho I/O. Nhiều quy trình sẽ tiêu tốn nhiều tài nguyên hơn.

2.2. Cách sử dụng

Ở đây chúng tôi cũng giới thiệu ba cách phổ biến để sử dụng đa xử lý:

Cách đầu tiên là sử dụng trực tiếp lớp Process, bằng cách khởi tạo lớp multiprocessing.Process và truyền vào hàm đích và các tham số để tạo một quy trình.

import multiprocessing def worker(): print('Process is doing') if __name__ == '__main__': # Tạo tiến trình p = multiprocessing.Process(target=worker) # Bắt đầu tiến trình p.start() # Đợi quá trình hoàn tất p .join()

Cách thứ hai là xác định các tác vụ được thực hiện bởi tiến trình bằng cách kế thừa lớp multiprocessing.Process và ghi đè phương thức run.

nhập lớp đa xử lý MyProcess(multiprocessing.Process): def run(self): print(f'{self.name} tiến trình đang thực thi') if __name__ == '__main__': # Tạo một phiên bản tiến trình my_process = MyProcess() # Bắt đầu quy trình my_process.start() # Đợi quá trình hoàn tất my_process.join()

Cách cuối cùng là tạo một nhóm quy trình thông qua lớp multiprocessing.Pool, tự động gán nhiệm vụ cho các quy trình và cải thiện việc sử dụng tài nguyên.

import multiprocessing def task(num): return num * 2 if __name__ == '__main__': # Tạo một nhóm quy trình, số lượng quy trình tối đa là 3 với multiprocessing.Pool(processes=3) làm nhóm: # Sử dụng phương thức bản đồ để thực thi kết quả nhiệm vụ song song = pool.map(task, range(5)) print(results)

Ba phương pháp sử dụng này trông giống như phân luồng trong phần trước, nhưng quá trình xử lý cơ bản của chúng hoàn toàn khác nhau.

Đa xử lý tạo ra một quy trình riêng biệt để mỗi tác vụ thực thi; trong khi tất cả các tác vụ trong luồng đều được thực thi trong cùng một quy trình.

3. Không đồng bộ

Lịch sử của mô-đun không đồng bộ asyncio muộn hơn nhiều so với hai mô-đun trên. Nó được giới thiệu lần đầu tiên trong Python 3.4.

Trong Python 3.5, các từ khóa async và wait đã được giới thiệu, giúp việc viết mã không đồng bộ ngắn gọn hơn và dễ đọc hơn, cải thiện đáng kể trải nghiệm lập trình không đồng bộ và thúc đẩy ứng dụng rộng rãi của asyncio.

3.1 Tình huống sử dụng và hạn chế

asyncio phù hợp với các tình huống yêu cầu xử lý đồng thời cao sau:

  • Trình thu thập dữ liệu web: Khi thu thập dữ liệu nhiều trang web, asyncio có thể tiếp tục gửi các yêu cầu khác trong khi chờ phản hồi, cải thiện đáng kể hiệu quả thu thập dữ liệu và rút ngắn thời gian thu thập lượng lớn dữ liệu.

  • Phát triển máy chủ mạng: xử lý các kết nối máy khách có tính đồng thời cao, chẳng hạn như xây dựng máy chủ trò chuyện, dịch vụ đẩy dữ liệu thời gian thực, v.v. Nó có thể xử lý từng yêu cầu của khách hàng một cách không đồng bộ để tránh bị chặn và đảm bảo rằng máy chủ chạy hiệu quả.

  • Các tác vụ chuyên sâu về I/O: chẳng hạn như đọc và ghi tệp, vận hành cơ sở dữ liệu, v.v. asyncio có thể thực hiện các tác vụ khác trong khi chờ các thao tác I/O hoàn tất, giảm thời gian chờ tổng thể và cải thiện hiệu suất chương trình.

Tất nhiên, asyncio có những ưu điểm rõ ràng nhưng cũng có một số hạn chế.

Một mặt, vì dựa trên một luồng duy nhất nên nó có hiệu năng kém khi xử lý các tác vụ ngốn CPU và không thể tận dụng hết lợi thế của CPU đa lõi.

Mặt khác, mô hình lập trình không đồng bộ tương đối phức tạp, việc gỡ lỗi và bảo trì mã rất khó khăn. Các nhà phát triển cần có hiểu biết sâu sắc về các khái niệm không đồng bộ, nếu không sẽ dễ xảy ra lỗi logic.

Ngoài ra, asyncio có thể gặp vấn đề về khả năng tương thích với một số thư viện đồng bộ hóa truyền thống và bạn có thể gặp khó khăn khi tích hợp mã hiện có.

3.2. Cách sử dụng

asyncio là một mô-đun tương đối mới. Công dụng chính của nó là:

  1. Xác định hàm coroutine bằng cách sử dụngđộ phân giải không đồng bộKhai báo từ khóa, được sử dụng bên trong hàmchờ đợiTừ khóa tạm dừng quá trình thực thi coroutine và chờ các hoạt động không đồng bộ khác hoàn tất.
import asyncio async def coroutine(): print('Bắt đầu thực thi hàm coroutine') đang chờ asyncio.sleep(1) print('Việc thực thi hàm coroutine kết thúc') if __name__ == '__main__': asyncio.run(coroutine() )

asyncio.run() được dùng để chạy coroutine cấp cao nhất.

  1. sử dụngasyncio.gather()Một hàm có thể chạy nhiều coroutine cùng một lúc.
import asyncio async def coroutine1(): wait asyncio.sleep(1) print('Coroutine 1 đã được thực thi') async def coroutine2(): wait asyncio.sleep(2) print('Coroutine 2 đã được thực thi') if __name__ == "__main__": thử: loop = asyncio.get_running_loop() ngoại trừ RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) thử: loop.run_until_complete(asyncio.gather(coroutine1(), coroutine2())) cuối cùng: loop.close()
  1. sử dụngkhông đồng bộ choLặp lại trên một đối tượng có thể lặp không đồng bộ.
nhập asyncio async def async_generator(): for i in range(3): chờ asyncio.sleep(1) mang lại i async def main(): async for num in async_generator(): print(num) if __name__ == "__main__" : asyncio.run(main())

Phương pháp này phù hợp để xử lý các chuỗi dữ liệu được tạo không đồng bộ.

4. Tóm tắt

Tổng thể, .

Đa luồng là việc tạo ra nhiều luồng trong một quy trình để chia sẻ tài nguyên. Chi phí chuyển đổi luồng thấp và phù hợp với các tác vụ đòi hỏi nhiều I/O, chẳng hạn như yêu cầu mạng và đọc và ghi tệp.

Nó rất đơn giản để lập trình và có thể cải thiện khả năng phản hồi của chương trình, nhưng do khóa trình thông dịch toàn cầu nên nó không thể tận dụng được tính năng đa lõi trong các tác vụ sử dụng nhiều CPU và cũng có các vấn đề về an toàn luồng.

Mỗi tiến trình trong đa tiến trình có bộ nhớ và tài nguyên độc lập, phù hợp với các tác vụ sử dụng nhiều CPU. Nó có thể tận dụng tối đa CPU đa lõi và có độ ổn định cao.

Tuy nhiên, việc tạo và hủy quá trình rất tốn kém, đồng thời việc liên lạc và chia sẻ dữ liệu giữa các quá trình rất phức tạp.

Lập trình không đồng bộ dựa trên các vòng lặp sự kiện và coroutine, đạt được khả năng thực thi không đồng bộ trong một luồng duy nhất.

Nó có hiệu suất xử lý đồng thời cao, mã ngắn gọn và phù hợp với số lượng lớn các tác vụ đòi hỏi nhiều I/O. Nhưng nó không phù hợp với các tác vụ sử dụng nhiều CPU, mô hình lập trình phức tạp, việc gỡ lỗi và bảo trì rất khó khăn.

Nói một cách đơn giản, trong quá trình phát triển, hãy sử dụng ít đa luồng hơn cho các tác vụ cần nhiều I/O và nhiều tác vụ không đồng bộ hơn cho các tác vụ sử dụng nhiều CPU và kết hợp các tác vụ hỗn hợp theo yêu cầu;

Cuối cùng, bài viết này về tóm tắt đồng thời Python: lập trình đa luồng, đa quy trình và không đồng bộ kết thúc tại đây. Nếu bạn muốn biết thêm về tóm tắt đồng thời Python: lập trình đa luồng, đa quy trình và không đồng bộ, vui lòng tìm kiếm bài viết CFSDN. Hoặc tiếp tục duyệt các bài viết liên quan, tôi hy vọng bạn sẽ ủng hộ blog của tôi trong tương lai! .

57 4 0
Chứng chỉ ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com
Xem sitemap của VNExpress