Hướng dẫn python reuse threadpoolexecutor - python sử dụng lại threadpoolexecutor

Python ThreadPoolExecutor cho phép bạn tạo và quản lý các nhóm luồng trong Python.Python ThreadPoolExecutor allows you to create and manage thread pools in Python.

Show

Mặc dù ThreadPoolExecutor đã có sẵn từ Python 3.2, nhưng nó không được sử dụng rộng rãi, có lẽ vì những hiểu lầm về khả năng và hạn chế của các chủ đề trong Python.ThreadPoolExecutor has been available since Python 3.2, it is not widely used, perhaps because of misunderstandings of the capabilities and limitations of Threads in Python.

Hướng dẫn này cung cấp một đánh giá chi tiết và toàn diện về ThreadPoolExecutor trong Python, bao gồm cách thức hoạt động, cách sử dụng nó, các câu hỏi phổ biến và thực tiễn tốt nhất.ThreadPoolExecutor in Python, including how it works, how to use it, common questions, and best practices.

Đây là một hướng dẫn lớn hơn 23.000 từ. Bạn có thể muốn đánh dấu nó để bạn có thể tham khảo nó khi bạn phát triển các chương trình đồng thời của mình.

Hãy để lặn trong.

  • Chủ đề Python và nhu cầu về các nhóm chủ đề
    • Chủ đề Python là gì
    • Nhóm chủ đề là gì
  • ThreadPoolExecutor cho các nhóm chủ đề trong Python
    • Người thực thi là gì
    • Tương lai là gì
  • Vòng đời của ThreadPoolExecutor
    • Bước 1. Tạo nhóm chủ đề
    • Bước 2. Gửi nhiệm vụ đến nhóm chủ đề
    • Bước 3. Chờ các nhiệm vụ hoàn thành (tùy chọn)
    • Bước 4. Tắt nhóm chủ đề
    • Trình quản lý bối cảnh ThreadPoolExecutor
  • Ví dụ về ThreadPoolExecutor
    • Tải xuống các tập tin một cách tự do
    • Tải xuống các tệp đồng thời với Sublding ()
    • Tải xuống các tệp đồng thời với SUMAL () và AS_COMPLETED ()
  • Các mẫu sử dụng ThreadPoolExecutor
    • Bản đồ và mô hình chờ
    • Gửi và sử dụng như đã hoàn thành
    • Gửi và sử dụng tuần tự
    • Gửi và sử dụng gọi lại
    • Gửi và chờ tất cả
    • Gửi và đợi đầu tiên
  • Cách định cấu hình ThreadPoolExecutor
    • Định cấu hình số lượng luồng
    • Bạn nên sử dụng bao nhiêu chủ đề?
    • Định cấu hình tên luồng
    • Định cấu hình trình khởi tạo
  • Cách sử dụng chi tiết các đối tượng trong tương lai
    • Vòng đời của một đối tượng tương lai
    • Cách kiểm tra trạng thái của tương lai
    • Làm thế nào để có được kết quả từ tương lai
    • Cách hủy bỏ tương lai
    • Cách thêm một cuộc gọi lại vào tương lai
    • Làm thế nào để có được ngoại lệ từ tương lai
  • Khi nào nên sử dụng ThreadPoolExecutor
    • Sử dụng ThreadPoolExecutor khi
    • Sử dụng nhiều ThreadPoolExecutor khi
    • Don Tiết sử dụng ThreadPoolExecutor khi
    • Sử dụng các chủ đề cho các tác vụ gắn IO
    • Don Tiết sử dụng các chủ đề cho các nhiệm vụ ràng buộc CPU
  • ThreadPoolExecutor Xử lý ngoại lệ
    • Xử lý ngoại lệ trong quá trình khởi tạo luồng
    • Xử lý ngoại lệ trong quá trình thực hiện nhiệm vụ
    • Xử lý ngoại lệ trong các cuộc gọi lại
  • Làm thế nào để ThreadPoolExecutor hoạt động trong nội bộ
    • Nhiệm vụ được thêm vào hàng đợi nội bộ
    • Chủ đề công nhân được tạo khi cần thiết
  • ThreadPoolExecutor Thực tiễn tốt nhất
    • Sử dụng Trình quản lý bối cảnh
    • Sử dụng bản đồ () cho các vòng lặp không đồng bộ
    • Sử dụng SUBME () với as_completed ()
    • Sử dụng các chức năng độc lập làm nhiệm vụ
    • Sử dụng cho các nhiệm vụ ràng buộc IO (có thể)
  • Lỗi phổ biến khi sử dụng ThreadPoolExecutor
    • Sử dụng một cuộc gọi chức năng trong SUMMT ()
    • Sử dụng một cuộc gọi chức năng trong bản đồ ()
    • Chữ ký chức năng không chính xác cho bản đồ ()
    • Chữ ký chức năng không chính xác cho các cuộc gọi lại trong tương lai
  • Các câu hỏi phổ biến khi sử dụng ThreadPoolExecutor
    • Làm thế nào để bạn dừng một nhiệm vụ đang chạy?
    • Làm thế nào để bạn chờ đợi tất cả các nhiệm vụ hoàn thành?
    • Làm thế nào để bạn tự động thay đổi số lượng chủ đề?
    • Làm thế nào để bạn đăng nhập từ một nhiệm vụ?
    • Làm thế nào để bạn các nhiệm vụ đơn vị và nhóm chủ đề?
    • Làm thế nào để bạn so sánh hiệu suất nối tiếp với hiệu suất song song?
    • Làm thế nào để bạn đặt Chunksize trong bản đồ ()?
    • Làm thế nào để bạn gửi một nhiệm vụ tiếp theo?
    • Làm thế nào để bạn lưu trữ trạng thái địa phương cho mỗi chủ đề?
    • Làm thế nào để bạn thể hiện sự tiến bộ của tất cả các nhiệm vụ?
    • Chúng ta có cần phải kiểm tra __main__ không?
    • Làm thế nào để bạn có được một đối tượng trong tương lai cho các tác vụ được thêm vào bản đồ ()?
    • Tôi có thể gọi Shutdown () từ trong Trình quản lý ngữ cảnh không?
  • Phản đối phổ biến khi sử dụng ThreadPoolExecutor
    • Điều gì về khóa phiên dịch toàn cầu (GIL)?
    • Có phải chủ đề Python có phải là chủ đề thực tế không?
    • Aren sắt python chủ đề lỗi?
    • Có phải là Python Python là một lựa chọn tồi cho sự đồng thời?
    • Tại sao không luôn luôn sử dụng ProcessPoolExecutor thay thế?
    • Tại sao không sử dụng Threading.thread thay thế?
    • Tại sao không sử dụng asyncio?
  • Đọc thêm
    • Sách
    • API
    • Người giới thiệu
  • Kết luận

Chủ đề Python và nhu cầu về các nhóm chủ đề

Vì vậy, các chủ đề là gì và tại sao chúng ta quan tâm đến nhóm chủ đề?

Chủ đề Python là gì

Một chủ đề đề cập đến một chủ đề thực thi bởi một chương trình máy tính.

Mỗi chương trình Python là một quá trình với một luồng được gọi là luồng chính được sử dụng để thực hiện các hướng dẫn chương trình của bạn. Trên thực tế, mỗi quá trình là một trường hợp của trình thông dịch Python thực thi các hướng dẫn Python (mã byte Python), mức thấp hơn một chút so với mã bạn nhập vào chương trình Python của bạn.

Đôi khi, chúng ta có thể cần tạo các luồng bổ sung trong quy trình Python của chúng ta để thực hiện đồng thời các nhiệm vụ.

Python cung cấp các luồng ngây thơ (cấp độ hệ thống) thực sự thông qua lớp luồng.threading.Thread class.

Một tác vụ có thể được chạy trong một luồng mới bằng cách tạo một thể hiện của lớp luồng và chỉ định hàm để chạy trong luồng mới thông qua đối số đích.Thread class and specifying the function to run in the new thread via the target argument.

.....

# Tạo và định cấu hình một luồng mới để chạy chức năng

thread=Thread(target=task)=Thread(target=task)

Khi luồng được tạo, nó phải được bắt đầu bằng cách gọi hàm start ().start() function.

.....

# Tạo và định cấu hình một luồng mới để chạy chức năng

thread.start().start()

Sau đó, chúng ta có thể đợi xung quanh để hoàn thành nhiệm vụ bằng cách tham gia chủ đề; Ví dụ

.....

# Đợi nhiệm vụ hoàn thành

thread.join().join()

Chúng ta có thể chứng minh điều này với một ví dụ hoàn chỉnh với một nhiệm vụ ngủ một lúc và in một tin nhắn.

Ví dụ hoàn chỉnh về việc thực hiện chức năng tác vụ đích trong một luồng riêng biệt được liệt kê bên dưới.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

# SuperfastPython.com

# Ví dụ về thực thi chức năng tác vụ đích trong một luồng riêng biệt

từ thời gian nhập vào giấc ngủtime import sleep

từ luồng nhập luồngthreading import Thread

# một nhiệm vụ đơn giản chặn một lúc và in một tin nhắn

nhiệm vụ def ():task():

& nbsp; & nbsp; & nbsp; & nbsp;# block trong giây lát# block for a moment

    sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp;# hiển thị một thông báo# display a message

& nbsp; & nbsp; & nbsp; & nbsp; in ('Điều này đến từ một chủ đề khác')print('This is coming from another thread')

# Tạo và định cấu hình một luồng mới để chạy chức năng

thread=Thread(target=task)= Thread(target=task)

# Bắt đầu tác vụ trong một luồng mới

thread.start().start()

# Hiển thị tin nhắn

In ('Đang chờ chủ đề mới kết thúc ...')('Waiting for the new thread to finish...')

# Đợi nhiệm vụ hoàn thành

thread.join().join()

Chúng ta có thể chứng minh điều này với một ví dụ hoàn chỉnh với một nhiệm vụ ngủ một lúc và in một tin nhắn.task() function.

Ví dụ hoàn chỉnh về việc thực hiện chức năng tác vụ đích trong một luồng riêng biệt được liệt kê bên dưới.task() function is executed in another thread. The task sleeps for a moment; meanwhile, in the main thread, a message is printed that we are waiting around and the main thread joins the new thread.

# SuperfastPython.com

# Ví dụ về thực thi chức năng tác vụ đích trong một luồng riêng biệt

từ thời gian nhập vào giấc ngủ

từ luồng nhập luồng

  • # một nhiệm vụ đơn giản chặn một lúc và in một tin nhắn

nhiệm vụ def ():

& nbsp; & nbsp; & nbsp; & nbsp;# block trong giây lát

& nbsp; & nbsp; & nbsp; & nbsp;# hiển thị một thông báo

& nbsp; & nbsp; & nbsp; & nbsp; in ('Điều này đến từ một chủ đề khác')

# Tạo và định cấu hình một luồng mới để chạy chức năng

# Bắt đầu tác vụ trong một luồng mới

# Hiển thị tin nhắn

  • In ('Đang chờ chủ đề mới kết thúc ...')
  • Chạy ví dụ tạo đối tượng luồng để chạy hàm Task ().

Chủ đề được bắt đầu và hàm Task () được thực thi trong một luồng khác. Nhiệm vụ ngủ một lúc; Trong khi đó, trong chuỗi chính, một tin nhắn được in rằng chúng tôi đang chờ xung quanh và luồng chính tham gia vào luồng mới.

Cuối cùng, chủ đề mới kết thúc ngủ, in một tin nhắn và đóng. Chủ đề chính sau đó tiếp tục và cũng đóng lại vì không có thêm hướng dẫn để thực hiện.

Đang chờ chủ đề mới kết thúc ...

Điều này đến từ một chủ đề khác

Bạn có thể tìm hiểu thêm về các chủ đề Python trong hướng dẫn:

Chủ đề trong Python: Hướng dẫn hoàn chỉnh

Điều này rất hữu ích để chạy các tác vụ ad hoc một lần trong một luồng riêng biệt, mặc dù nó trở nên cồng kềnh khi bạn có nhiều tác vụ để chạy.ThreadPoolExecutor class.

Mỗi luồng được tạo đòi hỏi phải áp dụng các tài nguyên (ví dụ: bộ nhớ cho không gian ngăn xếp của luồng). Các chi phí tính toán để thiết lập các chủ đề có thể trở nên đắt đỏ nếu chúng ta đang tạo và phá hủy nhiều luồng nhiều lần cho các nhiệm vụ ad hoc.

Thay vào đó, chúng tôi muốn giữ các chủ đề công nhân xung quanh để tái sử dụng nếu chúng tôi dự kiến ​​sẽ chạy nhiều nhiệm vụ ad hoc trong suốt chương trình của chúng tôi.ThreadPoolExecutor Python class is used to create and manage thread pools and is provided in the concurrent.futures module.

Điều này có thể đạt được bằng cách sử dụng một nhóm chủ đề.

Nhóm chủ đề là gìThreadPoolExecutor class directly via thread.py. It may be interesting to dig into how the class works internally, perhaps after you are familiar with how it works from the outside.

Nhóm chủ đề là một mẫu lập trình để tự động quản lý một nhóm các chủ đề công nhân.ThreadPoolExecutor extends the Executor class and will return Future objects when it is called.

  • Executor: Lớp cha cho ThreadPoolExecutor xác định các hoạt động vòng đời cơ bản cho nhóm.: Parent class for the ThreadPoolExecutor that defines basic lifecycle operations for the pool.
  • Tương lai: Đối tượng được trả về khi gửi các tác vụ đến nhóm luồng có thể hoàn thành sau.: Object returned when submitting tasks to the thread pool that may complete later.

Hãy cùng xem xét kỹ hơn về người thực thi, tương lai và vòng đời của việc sử dụng lớp ThreadPoolExecutor.Executors, Futures, and the lifecycle of using the ThreadPoolExecutor class.

Người thực thi là gì

Lớp ThreadPoolExecutor mở rộng lớp thực thi trừu tượng.ThreadPoolExecutor class extends the abstract Executor class.

Lớp thực thi xác định ba phương thức được sử dụng để kiểm soát nhóm luồng của chúng tôi; Chúng là: Gửi (), Map () và Shutdown ().Executor class defines three methods used to control our thread pool; they are: submit(), map(), and shutdown().

  • Gửi (): gửi một hàm sẽ được thực thi và trả về một đối tượng trong tương lai.: Dispatch a function to be executed and return a future object.
  • Bản đồ (): Áp dụng một hàm cho một yếu tố có thể lặp lại.: Apply a function to an iterable of elements.
  • Shutdown (): Tắt người thực thi.: Shut down the executor.

Người thực thi được bắt đầu khi lớp được tạo và phải tắt rõ ràng bằng cách gọi Shutdown (), sẽ phát hành bất kỳ tài nguyên nào do người thực thi nắm giữ. Chúng tôi cũng có thể tự động tắt, nhưng chúng tôi sẽ xem xét điều đó một chút sau.Executor is started when the class is created and must be shut down explicitly by calling shutdown(), which will release any resources held by the Executor. We can also shut down automatically, but we will look at that a little later.

Các hàm Sublices () và MAP () được sử dụng để gửi các tác vụ cho người thực thi để thực thi không đồng bộ.submit() and map() functions are used to submit tasks to the Executor for asynchronous execution.

Hàm bản đồ () hoạt động giống như hàm Bản đồ tích hợp () và được sử dụng để áp dụng hàm cho từng phần tử trong một đối tượng có thể lặp lại, giống như một danh sách. Không giống như hàm Bản đồ tích hợp (), mỗi ứng dụng của hàm cho một phần tử sẽ xảy ra không đồng bộ thay vì tuần tự.map() function operates just like the built-in map() function and is used to apply a function to each element in an iterable object, like a list. Unlike the built-in map() function, each application of the function to an element will happen asynchronously instead of sequentially.

Hàm Substem () có hàm, cũng như bất kỳ đối số nào và sẽ thực thi không đồng bộ, mặc dù cuộc gọi trả về ngay lập tức và cung cấp một đối tượng trong tương lai.submit() function takes a function, as well as any arguments, and will execute it asynchronously, although the call returns immediately and provides a Future object.

Chúng tôi sẽ xem xét kỹ hơn về mỗi ba chức năng này trong một khoảnh khắc. Thứ nhất, một tương lai là gì?

Tương lai là gì

Một tương lai là một đối tượng đại diện cho một kết quả bị trì hoãn cho một nhiệm vụ không đồng bộ.

Nó cũng đôi khi được gọi là một lời hứa hoặc một sự chậm trễ. Nó cung cấp một bối cảnh cho kết quả của một nhiệm vụ có thể hoặc không thể thực hiện và một cách để có được kết quả một khi nó có sẵn.

Trong Python, đối tượng trong tương lai được trả về từ một người thực thi, chẳng hạn như một ThreadPoolExecutor khi gọi hàm SOUND () để gửi một nhiệm vụ để được thực thi không đồng bộ.Future object is returned from an Executor, such as a ThreadPoolExecutor when calling the submit() function to dispatch a task to be executed asynchronously.

Nói chung, chúng tôi không tạo ra các đối tượng trong tương lai; Chúng tôi chỉ nhận được chúng và chúng tôi có thể cần gọi các chức năng trên chúng.Future objects; we only receive them and we may need to call functions on them.

Luôn luôn có một đối tượng trong tương lai cho mỗi tác vụ được gửi vào ThreadPoolExecutor thông qua một cuộc gọi để gửi ().Future object for each task sent into the ThreadPoolExecutor via a call to submit().

Đối tượng trong tương lai cung cấp một số chức năng hữu ích để kiểm tra trạng thái của nhiệm vụ như: hủy (), Running () và Done () để xác định xem nhiệm vụ có bị hủy hay không, hiện đang chạy hoặc thực hiện xong.cancelled(), running(), and done() to determine if the task was cancelled, is currently running, or has finished execution.

  • Đã hủy (): Trả về đúng nếu tác vụ bị hủy trước khi được thực thi.: Returns True if the task was cancelled before being executed.
  • Running (): Trả về đúng nếu nhiệm vụ hiện đang chạy.: Returns True if the task is currently running.
  • DOME (): Trả về đúng nếu nhiệm vụ đã hoàn thành hoặc bị hủy.: Returns True if the task has completed or was cancelled.

Một nhiệm vụ đang chạy không thể bị hủy và một nhiệm vụ được thực hiện có thể đã bị hủy.

Một đối tượng trong tương lai cũng cung cấp quyền truy cập vào kết quả của tác vụ thông qua hàm kết quả (). Nếu một ngoại lệ được nêu ra trong khi thực hiện tác vụ, nó sẽ được nêu lại khi gọi hàm kết quả () hoặc có thể được truy cập thông qua hàm Exception ().Future object also provides access to the result of the task via the result() function. If an exception was raised while executing the task, it will be re-raised when calling the result() function or can be accessed via the exception() function.

  • Kết quả (): Truy cập kết quả từ việc chạy nhiệm vụ.: Access the result from running the task.
  • Exception (): Truy cập bất kỳ ngoại lệ được nêu ra trong khi chạy nhiệm vụ.: Access any exception raised while running the task.

Cả hai hàm kết quả () và ngoại lệ () đều cho phép thời gian chờ được chỉ định làm đối số, đó là số giây để chờ giá trị trả về nếu tác vụ chưa hoàn thành. Nếu thời gian chờ hết hạn, thì một thời gian sẽ được nâng lên.result() and exception() functions allow a timeout to be specified as an argument, which is the number of seconds to wait for a return value if the task is not yet complete. If the timeout expires, then a TimeoutError will be raised.

Cuối cùng, chúng tôi có thể muốn có nhóm luồng tự động gọi một chức năng sau khi hoàn thành nhiệm vụ.

Điều này có thể đạt được bằng cách gắn một cuộc gọi lại vào đối tượng tương lai cho tác vụ thông qua hàm add_done_callback ().Future object for the task via the add_done_callback() function.

  • add_done_callback (): Thêm chức năng gọi lại vào tác vụ sẽ được thực thi bởi nhóm luồng sau khi hoàn thành tác vụ.: Add a callback function to the task to be executed by the thread pool once the task is completed.

Chúng tôi có thể thêm nhiều cuộc gọi lại cho mỗi nhiệm vụ và chúng sẽ được thực thi theo thứ tự chúng được thêm vào. Nếu tác vụ đã hoàn thành trước khi chúng tôi thêm cuộc gọi lại, thì cuộc gọi lại được thực thi ngay lập tức.

Bất kỳ trường hợp ngoại lệ nào được nêu trong hàm gọi lại sẽ không ảnh hưởng đến nhóm nhiệm vụ hoặc nhóm luồng.

Chúng ta sẽ xem xét kỹ hơn đối tượng trong tương lai trong phần sau.Future object in a later section.

Bây giờ chúng tôi đã quen thuộc với chức năng của một ThreadPoolExecutor do lớp thực thi cung cấp và của các đối tượng trong tương lai được trả về bằng cách gọi gửi (), hãy để xem xét kỹ hơn về vòng đời của lớp ThreadPoolExecutor.ThreadPoolExecutor provided by the Executor class and of Future objects returned by calling submit(), let’s take a closer look at the lifecycle of the ThreadPoolExecutor class.

Bị nhầm lẫn bởi API lớp ThreadPoolExecutor? Tải xuống bảng cheat pdf miễn phí của tôi
Download my FREE PDF cheat sheet

Vòng đời của ThreadPoolExecutor

ThreadPoolExecutor cung cấp một nhóm các chủ đề công nhân chung.ThreadPoolExecutor provides a pool of generic worker threads.

ThreadPoolExecutor được thiết kế để dễ dàng và đơn giản để sử dụng.ThreadPoolExecutor was designed to be easy and straightforward to use.

Nếu đa luồng giống như việc truyền các bánh răng thay đổi trong xe, thì sử dụng luồng. Chủ đề là một hộp số thủ công (ví dụ: khó học và sử dụng) trong khi đồng thời.concurrency.futures.ThreadPoolExecutor is an automatic transmission (e.g. easy to learn and use).

  • Chủ đề.Thread: Threading thủ công trong Python.: Manual threading in Python.
  • Đồng thời.: Automatic or “just work” mode for threading in Python.

Có bốn bước chính trong vòng đời của việc sử dụng lớp ThreadPoolExecutor; Họ là: Tạo, gửi, chờ đợi và tắt máy.

  • 1. Tạo: Tạo nhóm luồng bằng cách gọi trình xây dựng ThreadPoolExecutor ().ThreadPoolExecutor().
  • 2. Gửi: Gửi nhiệm vụ và nhận tương lai bằng cách gọi gửi () hoặc map ().submit() or map().
  • 3. Đợi: Đợi và nhận kết quả khi hoàn thành nhiệm vụ (tùy chọn).
  • 4. Tắt máy: Tắt nhóm luồng bằng cách gọi tắt ().shutdown().

Hình dưới đây giúp hình dung vòng đời của lớp ThreadPoolExecutor.

Hướng dẫn python reuse threadpoolexecutor - python sử dụng lại threadpoolexecutor
ThreadPoolExecutor vòng đời

Hãy cùng xem xét kỹ hơn về từng bước vòng đời.

Bước 1. Tạo nhóm chủ đề

Đầu tiên, một thể hiện ThreadPoolExecutor phải được tạo.ThreadPoolExecutor instance must be created.

Khi một thể hiện của một ThreadPoolExecutor được tạo, nó phải được cấu hình với số lượng luồng cố định trong nhóm, tiền tố được sử dụng khi đặt tên cho từng luồng trong nhóm và tên của một hàm để gọi khi khởi tạo từng luồng cùng với bất kỳ đối số nào cho chức năng.ThreadPoolExecutor is created, it must be configured with the fixed number of threads in the pool, a prefix used when naming each thread in the pool, and the name of a function to call when initializing each thread along with any arguments for the function.

Nhóm được tạo với một luồng cho mỗi CPU trong hệ thống của bạn cộng với bốn. Điều này là tốt cho hầu hết các mục đích.

  • Tổng mặc định các luồng = (tổng số CPU) + 4

Ví dụ: nếu bạn có 4 CPU, mỗi CPU, mỗi CPU hiện đại (hầu hết các CPU hiện đại đều có điều này), thì Python sẽ thấy 8 CPU và sẽ phân bổ (8 + 4) hoặc 12 luồng cho nhóm theo mặc định.

.....

# Tạo một nhóm luồng với số lượng chủ đề công nhân mặc định

executor=ThreadPoolExecutor()=ThreadPoolExecutor()

Bạn nên kiểm tra ứng dụng của bạn để xác định số lượng luồng dẫn đến hiệu suất tốt nhất, bất cứ nơi nào từ một vài luồng đến hàng trăm luồng.

Thông thường không phải là một ý tưởng tốt để có hàng ngàn luồng vì nó có thể bắt đầu tác động đến lượng RAM có sẵn và dẫn đến một lượng lớn chuyển đổi giữa các luồng, điều này có thể dẫn đến hiệu suất tồi tệ hơn.

Cũng thảo luận về việc điều chỉnh số lượng chủ đề cho nhóm của bạn sau này nhiều hơn.

Bạn có thể chỉ định số lượng luồng để tạo trong nhóm thông qua đối số MAX_Workers; Ví dụ:

.....

# Tạo một nhóm luồng với số lượng chủ đề công nhân mặc định

executor=ThreadPoolExecutor(max_workers=10)=ThreadPoolExecutor(max_workers=10)

Bạn nên kiểm tra ứng dụng của bạn để xác định số lượng luồng dẫn đến hiệu suất tốt nhất, bất cứ nơi nào từ một vài luồng đến hàng trăm luồng.

Thông thường không phải là một ý tưởng tốt để có hàng ngàn luồng vì nó có thể bắt đầu tác động đến lượng RAM có sẵn và dẫn đến một lượng lớn chuyển đổi giữa các luồng, điều này có thể dẫn đến hiệu suất tồi tệ hơn.

Cũng thảo luận về việc điều chỉnh số lượng chủ đề cho nhóm của bạn sau này nhiều hơn.

Bạn có thể chỉ định số lượng luồng để tạo trong nhóm thông qua đối số MAX_Workers; Ví dụ:

# Tạo một nhóm chủ đề với 10 luồng công nhânmap() function is an asynchronous version of the built-in map() function for applying a function to each element in an iterable, like a list.

Bước 2. Gửi nhiệm vụ đến nhóm chủ đề

Khi nhóm luồng đã được tạo, bạn có thể gửi các tác vụ để thực thi không đồng bộ.map() when converting a for loop to run using one thread per loop iteration.

.....

# Tạo một nhóm luồng với số lượng chủ đề công nhân mặc định

Bạn nên kiểm tra ứng dụng của bạn để xác định số lượng luồng dẫn đến hiệu suất tốt nhất, bất cứ nơi nào từ một vài luồng đến hàng trăm luồng.=pool.map(my_task,my_items)# does not block

Thông thường không phải là một ý tưởng tốt để có hàng ngàn luồng vì nó có thể bắt đầu tác động đến lượng RAM có sẵn và dẫn đến một lượng lớn chuyển đổi giữa các luồng, điều này có thể dẫn đến hiệu suất tồi tệ hơn.my_task” is your function that you wish to execute and “my_items” is your iterable of objects, each to be executed by your “my_task” function.

Cũng thảo luận về việc điều chỉnh số lượng chủ đề cho nhóm của bạn sau này nhiều hơn.

Bạn có thể chỉ định số lượng luồng để tạo trong nhóm thông qua đối số MAX_Workers; Ví dụ:map() function will return an iterable immediately. This iterable can be used to access the results from the target task function as they are available in the order that the tasks were submitted (e.g. order of the iterable you provided).

.....

# Tạo một nhóm luồng với số lượng chủ đề công nhân mặc định

Bạn nên kiểm tra ứng dụng của bạn để xác định số lượng luồng dẫn đến hiệu suất tốt nhất, bất cứ nơi nào từ một vài luồng đến hàng trăm luồng.result inexecutor.map(my_task,my_items):

print(result)(result)

Thông thường không phải là một ý tưởng tốt để có hàng ngàn luồng vì nó có thể bắt đầu tác động đến lượng RAM có sẵn và dẫn đến một lượng lớn chuyển đổi giữa các luồng, điều này có thể dẫn đến hiệu suất tồi tệ hơn.map() via the “timeout” argument in seconds if you wish to impose a limit on how long you’re willing to wait for each task to complete as you’re iterating, after which a TimeOut error will be raised.

.....

# Tạo một nhóm luồng với số lượng chủ đề công nhân mặc định

# Tạo một nhóm luồng với số lượng chủ đề công nhân mặc định

Bạn nên kiểm tra ứng dụng của bạn để xác định số lượng luồng dẫn đến hiệu suất tốt nhất, bất cứ nơi nào từ một vài luồng đến hàng trăm luồng.result inexecutor.map(my_task,my_items,timeout=5):

Thông thường không phải là một ý tưởng tốt để có hàng ngàn luồng vì nó có thể bắt đầu tác động đến lượng RAM có sẵn và dẫn đến một lượng lớn chuyển đổi giữa các luồng, điều này có thể dẫn đến hiệu suất tồi tệ hơn.

print(result)(result)

Cũng thảo luận về việc điều chỉnh số lượng chủ đề cho nhóm của bạn sau này nhiều hơn.

Bạn có thể chỉ định số lượng luồng để tạo trong nhóm thông qua đối số MAX_Workers; Ví dụ:submit() function submits one task to the thread pool for execution.

# Tạo một nhóm chủ đề với 10 luồng công nhânFuture object immediately.

Bước 2. Gửi nhiệm vụ đến nhóm chủ đềFuture object is a promise to return the results from the task (if any) and provides a way to determine if a specific task has been completed or not.

.....

# Tạo một nhóm luồng với số lượng chủ đề công nhân mặc định

Bạn nên kiểm tra ứng dụng của bạn để xác định số lượng luồng dẫn đến hiệu suất tốt nhất, bất cứ nơi nào từ một vài luồng đến hàng trăm luồng.=executor.submit(my_task,arg1,arg2)# does not block

Thông thường không phải là một ý tưởng tốt để có hàng ngàn luồng vì nó có thể bắt đầu tác động đến lượng RAM có sẵn và dẫn đến một lượng lớn chuyển đổi giữa các luồng, điều này có thể dẫn đến hiệu suất tồi tệ hơn.my_task” is the function you wish to execute and “arg1” and “arg2” are the first and second arguments to pass to the “my_task” function.

Bạn có thể sử dụng hàm SOUNT () để gửi các tác vụ không thực hiện bất kỳ đối số nào; Ví dụ:submit() function to submit tasks that do not take any arguments; for example:

.....

# Gửi một nhiệm vụ không có đối số và nhận một đối tượng trong tương lai

Tương lai = Executor.submit (my_task)# không chặn=executor.submit(my_task)# does not block

Bạn có thể truy cập kết quả của tác vụ thông qua hàm result () trên đối tượng tương lai được trả về. Cuộc gọi này sẽ chặn cho đến khi hoàn thành nhiệm vụ.result() function on the returned Future object. This call will block until the task is completed.

.....

# Gửi một nhiệm vụ không có đối số và nhận một đối tượng trong tương lai

Tương lai = Executor.submit (my_task)# không chặn=future.result()# blocks

Bạn có thể truy cập kết quả của tác vụ thông qua hàm result () trên đối tượng tương lai được trả về. Cuộc gọi này sẽ chặn cho đến khi hoàn thành nhiệm vụ.result() via the “timeout” argument in seconds if you wish to impose a limit on how long you’re willing to wait for each task to complete, after which a TimeOut error will be raised.

.....

# Gửi một nhiệm vụ không có đối số và nhận một đối tượng trong tương lai

Tương lai = Executor.submit (my_task)# không chặn=future.result(timeout=5)# blocks

Bạn có thể truy cập kết quả của tác vụ thông qua hàm result () trên đối tượng tương lai được trả về. Cuộc gọi này sẽ chặn cho đến khi hoàn thành nhiệm vụ.

# Nhận kết quả từ một tương laiconcurrent.futures module provides two module utility functions for waiting for tasks via their Future objects.

result = future.result ()# khốiFuture objects are only created when we call submit() to push tasks into the thread pool.

Bạn cũng có thể đặt thời gian chờ khi gọi kết quả () thông qua đối số thời gian chờ của trực tuyến trong vài giây nếu bạn muốn áp đặt giới hạn về thời gian bạn sẵn sàng chờ đợi mỗi nhiệm vụ hoàn thành, sau đó sẽ tăng lỗi.map() or submit() or wait for all tasks in the thread pool to finish.

# Đợi nhiệm vụ hoàn thành hoặc hết thời gian hết hạnwait() for waiting for Future objects to complete and as_completed() for getting Future objects as their tasks complete.

  • result = future.Result (thời gian chờ = 5)# khối: Wait on one or more Future objects until they are completed.
  • Bước 3. Chờ các nhiệm vụ hoàn thành (tùy chọn): Returns Future objects from a collection as they complete their execution.

Mô -đun đồng thời.Future objects created by one or more thread pools, they are not specific to any given thread pool in your application. This is helpful if you want to perform waiting operations across multiple thread pools that are executing different types of tasks.

Hãy nhớ lại rằng các đối tượng trong tương lai chỉ được tạo khi chúng ta gọi Substem () để đẩy các tác vụ vào nhóm luồng.

.....

Các chức năng chờ này là tùy chọn để sử dụng, vì bạn có thể chờ kết quả trực tiếp sau khi gọi bản đồ () hoặc gửi () hoặc chờ tất cả các tác vụ trong nhóm luồng kết thúc.

Hai hàm mô -đun này đang chờ () để chờ các đối tượng trong tương lai hoàn thành và as_completed () để nhận các đối tượng trong tương lai khi các tác vụ của chúng hoàn tất.=[executor.submit(my_task,my_data)formy_data in my_datalist]

Đợi (): Đợi một hoặc nhiều đối tượng trong tương lai cho đến khi chúng được hoàn thành.my_data” is one element of data passed as an argument to “my_task“, and “my_datalist” is our source of my_data objects.

AS_Completed (): Trả về các đối tượng trong tương lai từ một bộ sưu tập khi chúng hoàn thành việc thực thi của chúng.Future objects to wait() or as_completed().

Bạn có thể sử dụng cả hai chức năng với các đối tượng trong tương lai được tạo bởi một hoặc nhiều nhóm luồng, chúng không cụ thể cho bất kỳ nhóm chủ đề nào trong ứng dụng của bạn. Điều này rất hữu ích nếu bạn muốn thực hiện các hoạt động chờ trên nhiều nhóm luồng đang thực hiện các loại tác vụ khác nhau.Future objects in this way is not required, just a common pattern when converting for loops into tasks submitted to a thread pool.

Cả hai chức năng đều hữu ích để sử dụng với một thành ngữ của việc gửi nhiều tác vụ vào nhóm luồng thông qua gửi trong nén danh sách; Ví dụ:

# gửi các nhiệm vụ vào nhóm chủ đề và tạo một danh sách các tương laiwait() function can take one or more Future objects and will return when a specified action occurs, such as all tasks completing, one task completing, or one task raising an exception.

Tương lai = [Executor.submit (my_task, my_data) formy_data inmy_datalist]Future objects that match the condition set via the “return_when“. The second set will contain all of the futures for tasks that did not meet the condition. These are called the “done” and the “not_done” sets of futures.

Ở đây, my_task là chức năng tác vụ mục tiêu tùy chỉnh của chúng tôi, thì My_Data, là một yếu tố của dữ liệu được truyền như một đối số cho MY_TASK, và Hồi My_Datalist là nguồn của chúng tôi về các đối tượng My_Data.

Sau đó, chúng ta có thể chuyển các đối tượng trong tương lai để chờ () hoặc as_completed ().FIRST_COMPLETED constant passed to the “return_when” argument.

.....

Tạo một danh sách các đối tượng trong tương lai theo cách này là không cần thiết, chỉ là một mẫu phổ biến khi chuyển đổi các vòng lặp thành các tác vụ được gửi đến một nhóm luồng.

done,not_done=wait(futures,return_when=concurrent.futures.FIRST_COMPLETED),not_done=wait(futures,return_when=concurrent.futures.FIRST_COMPLETED)

Bước 3a. Chờ tương lai để hoàn thànhALL_COMPLETED constant.

Hàm chờ () có thể lấy một hoặc nhiều đối tượng trong tương lai và sẽ quay lại khi xảy ra hành động được chỉ định, chẳng hạn như tất cả các tác vụ hoàn thành, một nhiệm vụ hoàn thành hoặc một tác vụ nâng cao ngoại lệ.submit() to dispatch tasks and are looking for an easy way to wait for all work to be completed.

.....

Hàm sẽ trả về một tập hợp các đối tượng trong tương lai phù hợp với điều kiện được đặt thông qua trò chơi trả về. Bộ thứ hai sẽ chứa tất cả các tương lai cho các nhiệm vụ không đáp ứng điều kiện. Chúng được gọi là các bộ tương lai đã hoàn thành trên mạng và các bộ tương lai của Not Not_Done.

done,not_done=wait(futures,return_when=concurrent.futures.ALL_COMPLETED),not_done=wait(futures,return_when=concurrent.futures.ALL_COMPLETED)

Nó rất hữu ích cho việc chờ đợi một loạt công việc lớn và ngừng chờ đợi khi chúng tôi nhận được kết quả đầu tiên.FIRST_EXCEPTION constant.

.....

Điều này có thể đạt được thông qua hằng số First_Completed được truyền đến đối số của Return Return_When.

done,not_done=wait(futures,return_when=concurrent.futures.FIRST_EXCEPTION),not_done=wait(futures,return_when=concurrent.futures.FIRST_EXCEPTION)

# Chờ cho đến khi chúng tôi nhận được kết quả đầu tiên

Ngoài ra, chúng ta có thể chờ tất cả các tác vụ hoàn thành thông qua hằng số all_completed.

Điều này có thể hữu ích nếu bạn đang sử dụng Substem () để gửi các nhiệm vụ và đang tìm kiếm một cách dễ dàng để chờ mọi công việc được hoàn thành.Future objects for tasks as they are completed in the thread pool.

# Đợi tất cả các nhiệm vụ hoàn thànhFuture objects created by calling submit() and it will return Future objects as they are completed in whatever order.

Ngoài ra còn có một tùy chọn để chờ ngoại lệ đầu tiên thông qua hằng số First_Exception.as_completed() function in a loop over the list of Future objects created when calling submit; for example:

.....

# Đợi ngoại lệ đầu tiên

Bước 3b. Chờ tương lai như đã hoàn thànhfuture inas_completed(futures):

Vẻ đẹp của việc thực hiện các nhiệm vụ đồng thời là chúng ta có thể nhận được kết quả khi chúng có sẵn, thay vì chờ đợi tất cả các nhiệm vụ được hoàn thành.

Tương lai = Executor.submit (my_task)# không chặn= future.result()# blocks

Bạn có thể truy cập kết quả của tác vụ thông qua hàm result () trên đối tượng tương lai được trả về. Cuộc gọi này sẽ chặn cho đến khi hoàn thành nhiệm vụ.map() in two ways. Firstly, map() returns an iterator over objects, not over Future objects. Secondly, map() returns results in the order that the tasks were submitted, not in the order that they are completed.

Bước 4. Tắt nhóm chủ đề

Khi tất cả các tác vụ được hoàn thành, chúng tôi có thể đóng xuống nhóm luồng, sẽ giải phóng từng luồng và bất kỳ tài nguyên nào nó có thể giữ (ví dụ: không gian ngăn xếp luồng).

.....

# Tắt máy nhóm chủ đề

Executor.shutdown ()# Blocks.shutdown()# blocks

Hàm Shutdown () sẽ chờ tất cả các tác vụ trong nhóm luồng hoàn thành trước khi quay lại theo mặc định.shutdown() function will wait for all tasks in the thread pool to complete before returning by default.

Hành vi này có thể được thay đổi bằng cách đặt đối số chờ đợi của Wikipedia thành Sai khi gọi Shutdown (), trong trường hợp đó, hàm sẽ quay lại ngay lập tức. Các tài nguyên được sử dụng bởi nhóm chủ đề sẽ không được phát hành cho đến khi tất cả các nhiệm vụ hiện tại và xếp hàng được hoàn thành.wait” argument to False when calling shutdown(), in which case the function will return immediately. The resources used by thread pool will not be released until all current and queued tasks are completed.

.....

# Tắt máy nhóm chủ đề

Executor.shutdown ()# Blocks.shutdown(wait=False)# does not blocks

Hàm Shutdown () sẽ chờ tất cả các tác vụ trong nhóm luồng hoàn thành trước khi quay lại theo mặc định.cancel_futures” argument to True. By default queued tasks are not cancelled when calling shutdown().

.....

# Tắt máy nhóm chủ đề

Executor.shutdown ()# Blocks.shutdown(cancel_futures=True)# blocks

Hàm Shutdown () sẽ chờ tất cả các tác vụ trong nhóm luồng hoàn thành trước khi quay lại theo mặc định.

Hành vi này có thể được thay đổi bằng cách đặt đối số chờ đợi của Wikipedia thành Sai khi gọi Shutdown (), trong trường hợp đó, hàm sẽ quay lại ngay lập tức. Các tài nguyên được sử dụng bởi nhóm chủ đề sẽ không được phát hành cho đến khi tất cả các nhiệm vụ hiện tại và xếp hàng được hoàn thành.

Executor.shutdown (chờ = false)# không chặnThreadPoolExecutor class is to use a context manager.

Chúng tôi cũng có thể hướng dẫn nhóm hủy bỏ tất cả các nhiệm vụ xếp hàng để ngăn chặn việc thực thi của họ. Điều này có thể đạt được bằng cách đặt đối số của Hủy hủy_futures thành True. Theo mặc định, các nhiệm vụ xếp hàng không bị hủy khi gọi Shutdown ().

# Hủy tất cả các nhiệm vụ xếp hàngThreadPoolExecutor with a context manager involves using the “with” keyword to create a block in which you can use the thread pool to execute tasks and get results.

Executor.Shutdown (Hủy_Futures = true)# Blocksshutdown() function with the default arguments, waiting for all queued and executing tasks to complete before returning and carrying on.

Nếu chúng ta quên đóng nhóm luồng, nhóm luồng sẽ được đóng tự động khi chúng ta thoát khỏi luồng chính. Nếu chúng ta quên đóng hồ bơi và vẫn còn các nhiệm vụ thực thi, luồng chính sẽ không thoát ra cho đến khi tất cả các tác vụ trong nhóm và tất cả các nhiệm vụ xếp hàng đã được thực hiện.

.....

# Tắt máy nhóm chủ đề

Executor.shutdown ()# BlocksThreadPoolExecutor(max_workers=10)aspool:

Hàm Shutdown () sẽ chờ tất cả các tác vụ trong nhóm luồng hoàn thành trước khi quay lại theo mặc định.

Hành vi này có thể được thay đổi bằng cách đặt đối số chờ đợi của Wikipedia thành Sai khi gọi Shutdown (), trong trường hợp đó, hàm sẽ quay lại ngay lập tức. Các tài nguyên được sử dụng bởi nhóm chủ đề sẽ không được phát hành cho đến khi tất cả các nhiệm vụ hiện tại và xếp hàng được hoàn thành.

Executor.shutdown (chờ = false)# không chặn

Chúng tôi cũng có thể hướng dẫn nhóm hủy bỏ tất cả các nhiệm vụ xếp hàng để ngăn chặn việc thực thi của họ. Điều này có thể đạt được bằng cách đặt đối số của Hủy hủy_futures thành True. Theo mặc định, các nhiệm vụ xếp hàng không bị hủy khi gọi Shutdown ().

# Hủy tất cả các nhiệm vụ xếp hàng

Executor.Shutdown (Hủy_Futures = true)# Blocks

Nếu chúng ta quên đóng nhóm luồng, nhóm luồng sẽ được đóng tự động khi chúng ta thoát khỏi luồng chính. Nếu chúng ta quên đóng hồ bơi và vẫn còn các nhiệm vụ thực thi, luồng chính sẽ không thoát ra cho đến khi tất cả các tác vụ trong nhóm và tất cả các nhiệm vụ xếp hàng đã được thực hiện.ThreadPoolExecutor, let’s look at some worked examples.


Trình quản lý bối cảnh ThreadPoolExecutor

Một cách ưa thích để làm việc với lớp ThreadPoolExecutor là sử dụng Trình quản lý ngữ cảnh.

Điều này phù hợp với cách ưa thích để làm việc với các tài nguyên khác, chẳng hạn như các tệp và ổ cắm.

Sử dụng ThreadPoolExecutor với Trình quản lý ngữ cảnh liên quan đến việc sử dụng từ khóa của With với từ khóa để tạo một khối trong đó bạn có thể sử dụng nhóm luồng để thực hiện các tác vụ và nhận kết quả.
 


Khi khối đã hoàn thành, nhóm luồng sẽ tự động tắt. Trong nội bộ, Trình quản lý ngữ cảnh sẽ gọi hàm Shutdown () với các đối số mặc định, chờ tất cả các nhiệm vụ được xếp hàng và thực hiện hoàn thành trước khi trả lại và tiếp tục.

Dưới đây là một đoạn mã để chứng minh việc tạo một nhóm luồng bằng trình quản lý ngữ cảnh.ThreadPoolExecutor.

# Tạo một nhóm chủ đềThreadPoolExecutor is to download files from the internet concurrently.

với ThreadPoolExecutor (MAX_Workers = 10) Aspool:ThreadPoolExecutor for downloading files concurrently.

# Gửi nhiệm vụ và nhận kết quả

# ...

# tự động tắt nhóm chủ đề ...

# hồ bơi đang tắt vào thời điểm này

Đây là một thành ngữ rất tiện dụng nếu bạn đang chuyển đổi một vòng lặp để được đa luồng.

Sẽ ít hữu ích hơn nếu bạn muốn nhóm chủ đề hoạt động trong nền trong khi bạn thực hiện các công việc khác trong chuỗi chính của chương trình hoặc nếu bạn muốn sử dụng lại nhóm chủ đề nhiều lần trong suốt chương trình của bạn.

Bây giờ chúng ta đã quen thuộc với cách sử dụng ThreadPoolExecutor, hãy để Lôi nhìn vào một số ví dụ đã làm việc.

URLS=['https://docs.python.org/3/library/concurrency.html',=['https://docs.python.org/3/library/concurrency.html',

        'https://docs.python.org/3/library/concurrent.html','https://docs.python.org/3/library/concurrent.html',

        'https://docs.python.org/3/library/concurrent.futures.html','https://docs.python.org/3/library/concurrent.futures.html',

        'https://docs.python.org/3/library/threading.html','https://docs.python.org/3/library/threading.html',

        'https://docs.python.org/3/library/multiprocessing.html','https://docs.python.org/3/library/multiprocessing.html',

        'https://docs.python.org/3/library/multiprocessing.shared_memory.html','https://docs.python.org/3/library/multiprocessing.shared_memory.html',

        'https://docs.python.org/3/library/subprocess.html','https://docs.python.org/3/library/subprocess.html',

        'https://docs.python.org/3/library/queue.html','https://docs.python.org/3/library/queue.html',

        'https://docs.python.org/3/library/sched.html','https://docs.python.org/3/library/sched.html',

        'https://docs.python.org/3/library/contextvars.html']'https://docs.python.org/3/library/contextvars.html']

URL rất dễ dàng để tải xuống trong Python.

Đầu tiên, chúng ta có thể cố gắng mở kết nối với máy chủ bằng hàm urllib.request.urropen () và chỉ định URL và thời gian chờ hợp lý trong giây.

Điều này sẽ cung cấp một kết nối, sau đó chúng ta có thể gọi hàm read () để đọc nội dung của tệp. Sử dụng trình quản lý bối cảnh cho kết nối sẽ đảm bảo nó sẽ được đóng tự động, ngay cả khi một ngoại lệ được nâng lên.read() function to read the contents of the file. Using the context manager for the connection will ensure it will be closed automatically, even if an exception is raised.

Hàm download_url () bên dưới thực hiện điều này, lấy URL làm tham số và trả về nội dung của tệp hoặc không có nếu tệp không thể được tải xuống vì bất kỳ lý do gì. Chúng tôi sẽ đặt thời gian chờ dài 3 giây trong trường hợp kết nối Internet của chúng tôi là bong tróc vì một số lý do.download_url() function below implements this, taking a URL as a parameter and returning the contents of the file or None if the file cannot be downloaded for whatever reason. We will set a lengthy timeout of 3 seconds in case our internet connection is flaky for some reason.

# Tải xuống URL và trả về dữ liệu thô hoặc không có lỗi

def download_url (url):download_url(url):

    try:try:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# open a connection to the server

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with urlopen(url,timeout=3)asconnection:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# read the contents of the html doc

            returnconnection.read()return connection.read()

    except:except:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# bad url, socket timeout, http forbidden, etc.

        returnNonereturnNone

Khi chúng tôi có dữ liệu cho một URL, chúng tôi có thể lưu nó dưới dạng tệp cục bộ.

Đầu tiên, chúng ta cần truy xuất tên tệp của tệp được chỉ định trong URL. Có một vài cách để làm điều này, nhưng hàm Os.Path.basename () là một cách tiếp cận phổ biến khi làm việc với các đường dẫn. Sau đó, chúng ta có thể sử dụng hàm Os.Path.join () để xây dựng đường dẫn đầu ra để lưu tệp, sử dụng thư mục chúng tôi chỉ định và tên tệp.os.path.basename() function is a common approach when working with paths. We can then use the os.path.join() function to construct an output path for saving the file, using a directory we specify and the filename.

Sau đó, chúng ta có thể sử dụng hàm tích hợp mở () để mở tệp ở chế độ ghi nhị phân và lưu nội dung của tệp, một lần nữa sử dụng trình quản lý ngữ cảnh để đảm bảo tệp được đóng sau khi chúng ta kết thúc.open() built-in function to open the file in write-binary mode and save the contents of the file, again using the context manager to ensure the file is closed once we are finished.

Hàm save_file () bên dưới thực hiện điều này, lấy URL đã được tải xuống, nội dung của tệp đã được tải xuống và đường dẫn đầu ra cục bộ nơi chúng tôi muốn lưu các tệp đã tải xuống. Nó trả về đường dẫn đầu ra được sử dụng để lưu tệp, trong trường hợp chúng tôi muốn báo cáo tiến trình cho người dùng.save_file() function below implements this, taking the URL that was downloaded, the contents of the file that was downloaded, and the local output path where we wish to save downloaded files. It returns the output path that was used to save the file, in case we want to report progress to the user.

# Lưu dữ liệu vào tệp cục bộ

def save_file (url, dữ liệu, đường dẫn):save_file(url,data,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận tên của tệp từ URL# get the name of the file from the url

    filename=basename(url)filename= basename(url)

& nbsp; & nbsp; & nbsp; & nbsp;# xây dựng một đường dẫn cục bộ để lưu tệp# construct a local path for saving the file

    outpath=join(path,filename)outpath=join(path,filename)

& nbsp; & nbsp; & nbsp; & nbsp;# lưu vào tệp# save to file

& nbsp; & nbsp; & nbsp; & nbsp; với Open (Outpath, 'WB')with open(outpath,'wb')asfile:

        file.write(data)file.write(data)

    returnoutpathreturnoutpath

Tiếp theo, chúng ta có thể gọi hàm download_url () cho một url đã cho trong danh sách của chúng tôi sau đó save_file () để lưu từng tệp đã tải xuống.download_url() function for a given URL in our list then save_file() to save each downloaded file.

Hàm download_and_save () bên dưới thực hiện điều này, báo cáo tiến trình trên đường đi và xử lý trường hợp của các URL không thể tải xuống.download_and_save() function below implements this, reporting progress along the way, and handling the case of URLs that cannot be downloaded.

# Tải xuống và lưu URL dưới dạng tệp cục bộ

def download_and_save (url, đường dẫn):download_and_save(url,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Tải xuống URL# download the url

    data=download_url(url)data=download_url(url)

& nbsp; & nbsp; & nbsp; & nbsp;# kiểm tra không có dữ liệu# check for no data

& nbsp; & nbsp; & nbsp; & nbsp; ifdata isnone:ifdata isNone:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print(f'>Error downloading {url}')

        returnreturn

& nbsp; & nbsp; & nbsp; & nbsp;# lưu dữ liệu vào một tệp cục bộ# save the data to a local file

    outpath=save_file(url,data,path)outpath=save_file(url,data,path)

& nbsp; & nbsp; & nbsp; & nbsp;# Báo cáo tiến trình# report progress

& nbsp; & nbsp; & nbsp; & nbsp; print (f '> Đã lưu {url} sang {outpath}')print(f'>Saved {url} to {outpath}')

Cuối cùng, chúng tôi cần một chức năng để thúc đẩy quá trình.

Đầu tiên, vị trí đầu ra cục bộ nơi chúng tôi sẽ lưu các tệp cần được tạo, nếu nó không tồn tại. Chúng ta có thể đạt được điều này bằng cách sử dụng hàm os.makedirs ().

Chúng tôi có thể lặp lại một danh sách các URL và gọi hàm Tải xuống_and_save () của chúng tôi cho mỗi danh sách.download_and_save() function for each.

Hàm download_docs () bên dưới thực hiện điều này.download_docs() function below implements this.

# Tải xuống danh sách các URL vào các tệp cục bộ

def download_docs (url, đường dẫn):download_docs(urls,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Tạo thư mục cục bộ, nếu cần# create the local directory, if needed

    makedirs(path,exist_ok=True)makedirs(path, exist_ok=True)

& nbsp; & nbsp; & nbsp; & nbsp;# tải xuống mỗi url và lưu dưới dạng tệp cục bộ# download each url and save as a local file

& nbsp; & nbsp; & nbsp; & nbsp; forurl inurls:forurl inurls:

        download_and_save(url,path)download_and_save(url, path)

Và đó là nó.

Sau đó, chúng tôi có thể gọi Download_Docs () của chúng tôi với danh sách URL và thư mục đầu ra của chúng tôi. Trong trường hợp này, chúng tôi sẽ sử dụng ‘Tài liệu/SIBDITORY của thư mục làm việc hiện tại của chúng tôi (nơi đặt tập lệnh Python) làm thư mục đầu ra.download_docs() with our list of URLs and an output directory. In this case, we will use a ‘docs/‘ subdirectory of our current working directory (where the Python script is located) as the output directory.

Kết hợp điều này lại với nhau, ví dụ hoàn chỉnh về việc tải xuống các tệp được liệt kê dưới đây.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

# SuperfastPython.com

# Tải xuống các tệp tài liệu và lưu vào các tệp cục bộ một cách tự do

từ hệ điều hành nhập khẩu makedirsos import makedirs

Từ Os.Path Nhập Basenameos.path import basename

từ Os.Path Nhập tham giaos.path import join

từ Urllib.Request Nhập Urlopenurllib.request import urlopen

# Tải xuống URL và trả về dữ liệu thô hoặc không có lỗi

def download_url (url):download_url(url):

    try:try:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# open a connection to the server

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with urlopen(url,timeout=3)asconnection:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# read the contents of the html doc

            returnconnection.read()return connection.read()

    except:except:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# bad url, socket timeout, http forbidden, etc.

        returnNonereturnNone

# Lưu dữ liệu vào tệp cục bộ

def save_file (url, dữ liệu, đường dẫn):save_file(url,data,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận tên của tệp từ URL# get the name of the file from the url

    filename=basename(url)filename=basename(url)

& nbsp; & nbsp; & nbsp; & nbsp;# xây dựng một đường dẫn cục bộ để lưu tệp# construct a local path for saving the file

    outpath=join(path,filename)outpath=join(path,filename)

& nbsp; & nbsp; & nbsp; & nbsp;# lưu vào tệp# save to file

& nbsp; & nbsp; & nbsp; & nbsp; với Open (Outpath, 'WB')with open(outpath, 'wb')asfile:

        file.write(data)file.write(data)

    returnoutpathreturnoutpath

# Tải xuống và lưu URL dưới dạng tệp cục bộ

def download_and_save (url, đường dẫn):download_and_save(url,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Tải xuống URL# download the url

    data=download_url(url)data=download_url(url)

& nbsp; & nbsp; & nbsp; & nbsp;# kiểm tra không có dữ liệu# check for no data

& nbsp; & nbsp; & nbsp; & nbsp; ifdata isnone:if data isNone:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print(f'>Error downloading {url}')

        returnreturn

& nbsp; & nbsp; & nbsp; & nbsp;# lưu dữ liệu vào một tệp cục bộ# save the data to a local file

    outpath=save_file(url,data,path)outpath =save_file(url,data,path)

& nbsp; & nbsp; & nbsp; & nbsp;# Báo cáo tiến trình# report progress

& nbsp; & nbsp; & nbsp; & nbsp; print (f '> Đã lưu {url} sang {outpath}')print(f'>Saved {url} to {outpath}')

# Tải xuống danh sách các URL vào các tệp cục bộ

def download_docs (url, đường dẫn):download_docs(urls,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Tạo thư mục cục bộ, nếu cần# create the local directory, if needed

    makedirs(path,exist_ok=True)makedirs(path,exist_ok=True)

& nbsp; & nbsp; & nbsp; & nbsp;# tải xuống mỗi url và lưu dưới dạng tệp cục bộ# download each url and save as a local file

& nbsp; & nbsp; & nbsp; & nbsp; forurl inurls:forurl inurls:

        download_and_save(url,path)download_and_save(url,path)

# Tài liệu API đồng thời Python

URLS=['https://docs.python.org/3/library/concurrency.html',= ['https://docs.python.org/3/library/concurrency.html',

        'https://docs.python.org/3/library/concurrent.html','https://docs.python.org/3/library/concurrent.html',

        'https://docs.python.org/3/library/concurrent.futures.html','https://docs.python.org/3/library/concurrent.futures.html',

        'https://docs.python.org/3/library/threading.html','https://docs.python.org/3/library/threading.html',

        'https://docs.python.org/3/library/multiprocessing.html','https://docs.python.org/3/library/multiprocessing.html',

        'https://docs.python.org/3/library/multiprocessing.shared_memory.html','https://docs.python.org/3/library/multiprocessing.shared_memory.html',

        'https://docs.python.org/3/library/subprocess.html','https://docs.python.org/3/library/subprocess.html',

        'https://docs.python.org/3/library/queue.html','https://docs.python.org/3/library/queue.html',

        'https://docs.python.org/3/library/sched.html','https://docs.python.org/3/library/sched.html',

        'https://docs.python.org/3/library/contextvars.html']'https://docs.python.org/3/library/contextvars.html']

# Đường dẫn cục bộ để lưu các tệp

PATH='docs'='docs'

# Tải xuống tất cả các tài liệu

download_docs(URLS,PATH)(URLS, PATH)

Chạy ví dụ lặp lại qua danh sách các URL và lần lượt tải xuống.

Mỗi tệp sau đó được lưu vào một tệp cục bộ trong thư mục được chỉ định.

Quá trình này mất khoảng 700 mili giây đến khoảng một giây (1.000 mili giây) trên hệ thống của tôi.

Hãy thử chạy nó một vài lần; Mất bao lâu trên hệ thống của bạn? Hãy cho tôi biết trong các ý kiến.
Let me know in the comments.

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library/concivers

> Đã lưu https://docs.python.org/3/l Library/Threading.html to Docs/Threading.html

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library/SubProcess.html to Docs/Subprocess.html

> Đã lưu https://docs.python.org/3/l Library/queue.html to Docs/queue.html

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library

Tiếp theo, chúng ta có thể xem xét việc làm cho chương trình đồng thời bằng cách sử dụng nhóm chủ đề.

Tải xuống các tệp đồng thời với Sublding ()

Chúng ta hãy xem xét việc cập nhật chương trình của chúng tôi để sử dụng ThempoolExecutor để tải xuống các tệp đồng thời.ThreadPoolExecutor to download files concurrently.

Một suy nghĩ đầu tiên có thể là sử dụng map () vì chúng tôi chỉ muốn thực hiện một vòng lặp đồng thời.map() as we just want to make a for-loop concurrent.

Thật không may, hàm download_and_save () mà chúng ta gọi là mỗi lần lặp trong vòng lặp có hai tham số, chỉ một trong số đó là một điều không thể điều chỉnh được.download_and_save() function that we call each iteration in the loop takes two parameters, only one of which is an iterable.

Một cách tiếp cận thay thế là sử dụng Substem () để gọi download_and_save () trong một luồng riêng cho mỗi URL trong danh sách được cung cấp.download_and_save() in a separate thread for each URL in the provided list.

Chúng ta có thể làm điều này bằng cách đầu tiên định cấu hình một nhóm luồng với số lượng luồng bằng số lượng URL trong danh sách. Chúng tôi sẽ sử dụng trình quản lý ngữ cảnh cho nhóm chủ đề để nó sẽ được đóng tự động cho chúng tôi khi chúng tôi hoàn thành.

Sau đó, chúng ta có thể gọi hàm SOUND () cho mỗi URL bằng cách sử dụng nén danh sách. Chúng tôi thậm chí không cần các đối tượng trong tương lai được trả lại từ việc gọi điện, vì không có kết quả là chúng tôi đang chờ đợi.submit() function for each URL using a list compression. We don’t even need the Future objects returned from calling submit, as there is no result we’re waiting for.

.....

# Tạo nhóm chủ đề

n_threads=len(urls)=len(urls)

với ThreadPoolExecutor (N_Threads) Asexecutor:ThreadPoolExecutor(n_threads)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# tải xuống mỗi url và lưu dưới dạng tệp cục bộ# download each url and save as a local file

& nbsp; & nbsp; & nbsp; & nbsp; _ = [Executor.submit (download_and_save, url, path) forurl inurls]_=[executor.submit(download_and_save,url,path)forurl inurls]

Khi mỗi chủ đề đã hoàn thành, trình quản lý bối cảnh sẽ đóng nhóm luồng cho chúng tôi và chúng tôi đã hoàn thành.

Chúng tôi thậm chí không cần thêm một cuộc gọi rõ ràng để chờ đợi, mặc dù chúng tôi có thể nếu chúng tôi muốn làm cho mã dễ đọc hơn; Ví dụ:

.....

# Tạo nhóm chủ đề

n_threads=len(urls)=len(urls)

với ThreadPoolExecutor (N_Threads) Asexecutor:ThreadPoolExecutor(n_threads)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# tải xuống mỗi url và lưu dưới dạng tệp cục bộ# download each url and save as a local file

& nbsp; & nbsp; & nbsp; & nbsp; _ = [Executor.submit (download_and_save, url, path) forurl inurls]futures=[executor.submit(download_and_save,url,path)forurl inurls]

Khi mỗi chủ đề đã hoàn thành, trình quản lý bối cảnh sẽ đóng nhóm luồng cho chúng tôi và chúng tôi đã hoàn thành.# wait for all download tasks to complete

    _,_=wait(futures)_,_=wait(futures)

Chúng tôi thậm chí không cần thêm một cuộc gọi rõ ràng để chờ đợi, mặc dù chúng tôi có thể nếu chúng tôi muốn làm cho mã dễ đọc hơn; Ví dụ:

& nbsp; & nbsp; & nbsp; & nbsp; futures = [Executor.submit (download_and_save, url, path) forurl inurls]download_docs() function that downloads and saves the files concurrently is listed below.

& nbsp; & nbsp; & nbsp; & nbsp;# chờ tất cả các nhiệm vụ tải xuống để hoàn tất

Nhưng, thêm sự chờ đợi này là không cần thiết.download_docs(urls,path):

Phiên bản cập nhật của chức năng Tải xuống_Docs () của chúng tôi tải xuống và lưu đồng thời các tệp được liệt kê dưới đây.# create the local directory, if needed

    makedirs(path,exist_ok=True)makedirs(path, exist_ok=True)

# Tải xuống danh sách các URL vào các tệp cục bộ# create the thread pool

    n_threads=len(urls)n_threads=len(urls)

def download_docs (url, đường dẫn):with ThreadPoolExecutor(n_threads)as executor:

& nbsp; & nbsp; & nbsp; & nbsp;# Tạo thư mục cục bộ, nếu cần# download each url and save as a local file

& nbsp; & nbsp; & nbsp; & nbsp;# tạo nhóm chủ đề_=[executor.submit(download_and_save,url, path)forurl inurls]

& nbsp;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;

& nbsp;

Tying này lại với nhau, ví dụ hoàn chỉnh được liệt kê dưới đây.os import makedirs

# SuperfastPython.comos.path import basename

# Tải xuống các tệp tài liệu và lưu đồng thời các tệp cục bộos.path import join

từ hệ điều hành nhập khẩu makedirsurllib.request import urlopen

Từ Os.Path Nhập Basenameconcurrent.futures import ThreadPoolExecutor

từ Os.Path Nhập tham gia

từ Urllib.Request Nhập Urlopendownload_url(url):

    try:try:

từ đồng thời.# open a connection to the server

# Tải xuống URL và trả về dữ liệu thô hoặc không có lỗiwith urlopen(url,timeout=3)asconnection:

def download_url (url):# read the contents of the html doc

            returnconnection.read()returnconnection.read()

    except:except:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# bad url, socket timeout, http forbidden, etc.

        returnNonereturn None

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;save_file(url,data,path):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# get the name of the file from the url

    filename=basename(url)filename= basename(url)

# Lưu dữ liệu vào tệp cục bộ# construct a local path for saving the file

    outpath=join(path,filename)outpath=join(path,filename)

& nbsp; & nbsp; & nbsp; & nbsp;# lưu vào tệp# save to file

& nbsp; & nbsp; & nbsp; & nbsp; với Open (Outpath, 'WB')with open(outpath,'wb')asfile:

        file.write(data)file.write(data)

    returnoutpathreturnoutpath

# Tải xuống và lưu URL dưới dạng tệp cục bộ

def download_and_save (url, đường dẫn):download_and_save(url,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Tải xuống URL# download the url

    data=download_url(url)data=download_url(url)

& nbsp; & nbsp; & nbsp; & nbsp;# kiểm tra không có dữ liệu# check for no data

& nbsp; & nbsp; & nbsp; & nbsp; ifdata isnone:if data isNone:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print(f'>Error downloading {url}')

        returnreturn

& nbsp; & nbsp; & nbsp; & nbsp;# lưu dữ liệu vào một tệp cục bộ# save the data to a local file

    outpath=save_file(url,data,path)outpath =save_file(url,data,path)

& nbsp; & nbsp; & nbsp; & nbsp;# Báo cáo tiến trình# report progress

& nbsp; & nbsp; & nbsp; & nbsp; print (f '> Đã lưu {url} sang {outpath}')print(f'>Saved {url} to {outpath}')

# Tải xuống danh sách các URL vào các tệp cục bộ

def download_docs (url, đường dẫn):download_docs(urls,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Tạo thư mục cục bộ, nếu cần# create the local directory, if needed

    makedirs(path,exist_ok=True)makedirs(path,exist_ok=True)

& nbsp; & nbsp; & nbsp; & nbsp;# tạo nhóm chủ đề# create the thread pool

    n_threads=len(urls)n_threads=len(urls)

& nbsp;with ThreadPoolExecutor(n_threads)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# download each url and save as a local file

& nbsp;_=[executor.submit(download_and_save,url,path)forurl inurls]

# Tài liệu API đồng thời Python

URLS=['https://docs.python.org/3/library/concurrency.html',=['https://docs.python.org/3/library/concurrency.html',

        'https://docs.python.org/3/library/concurrent.html','https://docs.python.org/3/library/concurrent.html',

        'https://docs.python.org/3/library/concurrent.futures.html','https://docs.python.org/3/library/concurrent.futures.html',

        'https://docs.python.org/3/library/threading.html','https://docs.python.org/3/library/threading.html',

        'https://docs.python.org/3/library/multiprocessing.html','https://docs.python.org/3/library/multiprocessing.html',

        'https://docs.python.org/3/library/multiprocessing.shared_memory.html','https://docs.python.org/3/library/multiprocessing.shared_memory.html',

        'https://docs.python.org/3/library/subprocess.html','https://docs.python.org/3/library/subprocess.html',

        'https://docs.python.org/3/library/queue.html','https://docs.python.org/3/library/queue.html',

        'https://docs.python.org/3/library/sched.html','https://docs.python.org/3/library/sched.html',

        'https://docs.python.org/3/library/contextvars.html']'https://docs.python.org/3/library/contextvars.html']

# Đường dẫn cục bộ để lưu các tệp

PATH='docs'='docs'

# Tải xuống tất cả các tài liệu

download_docs(URLS,PATH)(URLS, PATH)

Chạy ví dụ tải xuống và lưu các tệp như trước.

Lần này, hoạt động được hoàn thành trong một phần của một giây. Khoảng 300 mili giây trong trường hợp của tôi, ít hơn một nửa thời gian để tải xuống tất cả các tệp theo ví dụ trước.

Mất bao lâu để tải xuống tất cả các tệp trên hệ thống của bạn?

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library/concurrency.html to Docs/đồng thời

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library/queue.html to Docs/queue.html

> Đã lưu https://docs.python.org/3/l Library/concivers

> Đã lưu https://docs.python.org/3/l Library/Threading.html to Docs/Threading.html

> Đã lưu https://docs.python.org/3/l Library/SubProcess.html to Docs/Subprocess.html

> Đã lưu https://docs.python.org/3/l Library

Đây là một cách tiếp cận để thực hiện chương trình đồng thời, nhưng hãy để Lôi xem xét một số lựa chọn thay thế.

Tải xuống các tệp đồng thời với SUMAL () và AS_COMPLETED ()

Có lẽ chúng tôi muốn báo cáo tiến trình tải xuống khi chúng được hoàn thành.

Nhóm luồng cho phép chúng tôi thực hiện điều này bằng cách lưu trữ các đối tượng trong tương lai được trả về từ các cuộc gọi để gửi () và sau đó gọi AS_Completed () trên bộ sưu tập các đối tượng trong tương lai.Future objects returned from calls to submit() and then calling the as_completed() on the collection of Future objects.

Ngoài ra, hãy xem xét rằng chúng tôi đang làm hai điều trong nhiệm vụ. Đầu tiên là tải xuống từ một máy chủ từ xa, đây là hoạt động ràng buộc IO mà chúng ta có thể thực hiện đồng thời. Thứ hai là lưu nội dung của tệp vào ổ cứng cục bộ, đây là một hoạt động liên kết IO khác mà chúng ta không thể thực hiện đồng thời vì hầu hết các ổ cứng chỉ có thể lưu một tệp cùng một lúc.

Do đó, có lẽ một thiết kế tốt hơn là chỉ làm cho tập tin tải xuống một phần của chương trình thành một nhiệm vụ đồng thời và phần lưu tệp của chương trình nối tiếp.

Điều này sẽ đòi hỏi nhiều thay đổi hơn cho chương trình.

Chúng tôi có thể gọi hàm download_url () cho mỗi URL và đây có thể là tác vụ đồng thời của chúng tôi được gửi đến nhóm luồng.download_url() function for each URL and this can be our concurrent task submitted to the thread pool.

Khi chúng tôi gọi kết quả () trên mỗi đối tượng trong tương lai, nó sẽ cung cấp cho chúng tôi dữ liệu đã được tải xuống, nhưng chúng tôi đã giành được những gì URL dữ liệu đã được tải xuống. Đối tượng tương lai đã giành chiến thắng.result() on each Future object, it will give us the data that was downloaded, but we won’t know what URL the data was downloaded from. The Future object won’t know.

Do đó, chúng tôi có thể cập nhật tải xuống_url () để trả về cả dữ liệu đã được tải xuống và URL được cung cấp dưới dạng đối số.download_url() to return both the data that was downloaded and the URL that was provided as an argument.

Phiên bản cập nhật của hàm download_url () trả về một bộ dữ liệu và URL đầu vào được liệt kê bên dưới.download_url() function that returns a tuple of data and the input URL is listed below.

# Tải xuống URL và trả về dữ liệu thô hoặc không có lỗi

def download_url (url):download_url(url):

    try:try:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# open a connection to the server

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with urlopen(url,timeout=3)asconnection:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# read the contents of the html doc

            return(connection.read(),url)return (connection.read(),url)

    except:except:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# bad url, socket timeout, http forbidden, etc.

        return(None,url)return (None,url)

Sau đó, chúng tôi có thể gửi một cuộc gọi đến chức năng này cho mỗi URL đến nhóm luồng để cung cấp cho chúng tôi một đối tượng trong tương lai.Future object.

.....

# Tải xuống từng URL và lưu dưới dạng tệp cục bộ

Tương lai = [Executor.Submit (download_url, url) forurl inurls]=[executor.submit(download_url,url)forurl inurls]

Càng xa càng tốt.

Bây giờ, chúng tôi muốn lưu các tệp cục bộ và báo cáo tiến trình khi các tệp được tải xuống.

Điều này yêu cầu chúng ta ăn thịt chức năng download_and_save () và di chuyển nó trở lại chức năng download_docs () được sử dụng để điều khiển chương trình.download_and_save() function and move it back into the download_docs() function used to drive the program.

Chúng ta có thể lặp lại tương lai thông qua hàm as_completed () sẽ trả về các đối tượng trong tương lai theo thứ tự mà các bản tải xuống được hoàn thành, chứ không phải thứ tự chúng tôi gửi chúng vào nhóm luồng.as_completed() function that will return Future objects in the order that the downloads are completed, not the order that we dispatched them into the thread pool.

Sau đó, chúng ta có thể truy xuất dữ liệu và URL từ đối tượng tương lai.Future object.

.....

# Tải xuống từng URL và lưu dưới dạng tệp cục bộ

Tương lai = [Executor.Submit (download_url, url) forurl inurls]future inas_completed(futures):

Càng xa càng tốt.# get the downloaded url data

    data,url=future.result()data,url =future.result()

Bây giờ, chúng tôi muốn lưu các tệp cục bộ và báo cáo tiến trình khi các tệp được tải xuống.download_and_save() function.

.....

# Tải xuống từng URL và lưu dưới dạng tệp cục bộ

Tương lai = [Executor.Submit (download_url, url) forurl inurls]data isNone:

Càng xa càng tốt.print(f'>Error downloading {url}')

    continuecontinue

Bây giờ, chúng tôi muốn lưu các tệp cục bộ và báo cáo tiến trình khi các tệp được tải xuống.

outpath=save_file(url,data,path) =save_file(url,data,path)

Điều này yêu cầu chúng ta ăn thịt chức năng download_and_save () và di chuyển nó trở lại chức năng download_docs () được sử dụng để điều khiển chương trình.

Chúng ta có thể lặp lại tương lai thông qua hàm as_completed () sẽ trả về các đối tượng trong tương lai theo thứ tự mà các bản tải xuống được hoàn thành, chứ không phải thứ tự chúng tôi gửi chúng vào nhóm luồng.(f'>Saved {url} to {outpath}')

Sau đó, chúng ta có thể truy xuất dữ liệu và URL từ đối tượng tương lai.download_docs() function that will only download files concurrently then save the files serially as the files are downloaded is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# xử lý mỗi kết quả vì nó có sẵn

forfuture inas_completed (tương lai):download_docs(urls,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận dữ liệu URL đã tải xuống# create the local directory, if needed

    makedirs(path,exist_ok=True)makedirs(path, exist_ok=True)

Chúng tôi có thể kiểm tra xem việc tải xuống không thành công và báo cáo lỗi, nếu không hãy lưu tệp và báo cáo tiến độ theo bình thường. Một bản sao trực tiếp-dán từ hàm download_and_save ().# create the thread pool

    n_threads=len(urls)n_threads=len(urls)

# Kiểm tra không có dữ liệuwith ThreadPoolExecutor(n_threads)as executor:

Ifdata isnone:# download each url and save as a local file

& nbsp; & nbsp; & nbsp; & nbsp; in (f '> tải xuống lỗi {url}')futures=[executor.submit(download_url,url)for url inurls]

# Lưu dữ liệu vào tệp cục bộ# process each result as it is available

# Báo cáo tiến độforfuture inas_completed(futures):

print (f '> Đã lưu {url} sang {outpath}')# get the downloaded url data

            data,url=future.result()data,url=future.result()

Phiên bản cập nhật của chức năng Tải xuống_Docs () của chúng tôi sẽ chỉ tải xuống các tệp đồng thời sau đó lưu các tệp theo cách tự do khi các tệp được tải xuống được liệt kê bên dưới.# check for no data

# Tải xuống danh sách các URL vào các tệp cục bộif data isNone:

def download_docs (url, đường dẫn):print(f'>Error downloading {url}')

                continuecontinue

& nbsp; & nbsp; & nbsp; & nbsp;# Tạo thư mục cục bộ, nếu cần# save the data to a local file

            outpath=save_file(url,data,path)outpath=save_file(url,data,path)

& nbsp; & nbsp; & nbsp; & nbsp;# tạo nhóm chủ đề# report progress

& nbsp;print(f'>Saved {url} to {outpath}')

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

# SuperfastPython.com

# Tải xuống tài liệu đồng thời và lưu các tệp tại địa phương ser

từ hệ điều hành nhập khẩu makedirsos import makedirs

Từ Os.Path Nhập Basenameos.path import basename

từ Os.Path Nhập tham giaos.path import join

từ Urllib.Request Nhập Urlopenurllib.request import urlopen

từ đồng thời.concurrent.futures import ThreadPoolExecutor

từ đồng thời.futures nhập as_completedconcurrent.futures import as_completed

# Tải xuống URL và trả về dữ liệu thô hoặc không có lỗi

def download_url (url):download_url(url):

    try:try:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# open a connection to the server

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;with urlopen(url, timeout=3)asconnection:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# read the contents of the html doc

            return(connection.read(),url)return (connection.read(),url)

    except:except:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# bad url, socket timeout, http forbidden, etc.

        return(None,url)return (None,url)

# Lưu dữ liệu vào tệp cục bộ

def save_file (url, dữ liệu, đường dẫn):save_file(url,data,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận tên của tệp từ URL# get the name of the file from the url

    filename=basename(url)filename =basename(url)

& nbsp; & nbsp; & nbsp; & nbsp;# xây dựng một đường dẫn cục bộ để lưu tệp# construct a local path for saving the file

    outpath=join(path,filename)outpath=join(path,filename)

& nbsp; & nbsp; & nbsp; & nbsp;# lưu vào tệp# save to file

& nbsp; & nbsp; & nbsp; & nbsp; với Open (Outpath, 'WB')with open(outpath,'wb')asfile:

        file.write(data)file.write(data)

    returnoutpathreturnoutpath

# Tải xuống danh sách các URL vào các tệp cục bộ

def download_docs (url, đường dẫn):download_docs(urls,path):

& nbsp; & nbsp; & nbsp; & nbsp;# Tạo thư mục cục bộ, nếu cần# create the local directory, if needed

    makedirs(path,exist_ok=True)makedirs(path,exist_ok=True)

& nbsp; & nbsp; & nbsp; & nbsp;# tạo nhóm chủ đề# create the thread pool

    n_threads=len(urls)n_threads=len(urls)

& nbsp;with ThreadPoolExecutor(n_threads)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# download each url and save as a local file

& nbsp;futures=[executor.submit(download_url,url)forurl inurls]

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;## process each result as it is available

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;forfuture inas_completed(futures):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# get the downloaded url data

            data,url=future.result()data, url=future.result()

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# check for no data

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;ifdata isNone:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print(f'>Error downloading {url}')

                continuecontinue

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# save the data to a local file

            outpath=save_file(url,data,path)outpath= save_file(url,data,path)

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# report progress

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print(f'>Saved {url} to {outpath}')

# Tài liệu API đồng thời Python

URLS=['https://docs.python.org/3/library/concurrency.html',=['https://docs.python.org/3/library/concurrency.html',

        'https://docs.python.org/3/library/concurrent.html','https://docs.python.org/3/library/concurrent.html',

        'https://docs.python.org/3/library/concurrent.futures.html','https://docs.python.org/3/library/concurrent.futures.html',

        'https://docs.python.org/3/library/threading.html','https://docs.python.org/3/library/threading.html',

        'https://docs.python.org/3/library/multiprocessing.html','https://docs.python.org/3/library/multiprocessing.html',

        'https://docs.python.org/3/library/multiprocessing.shared_memory.html','https://docs.python.org/3/library/multiprocessing.shared_memory.html',

        'https://docs.python.org/3/library/subprocess.html','https://docs.python.org/3/library/subprocess.html',

        'https://docs.python.org/3/library/queue.html','https://docs.python.org/3/library/queue.html',

        'https://docs.python.org/3/library/sched.html','https://docs.python.org/3/library/sched.html',

        'https://docs.python.org/3/library/contextvars.html']'https://docs.python.org/3/library/contextvars.html']

# Đường dẫn cục bộ để lưu các tệp

PATH='docs'='docs'

# Tải xuống tất cả các tài liệu

download_docs(URLS,PATH)(URLS, PATH)

Chạy chương trình, các tệp được tải xuống và lưu như trước, có lẽ nhanh hơn vài mili giây.

Nhìn vào đầu ra của chương trình, chúng ta có thể thấy rằng thứ tự các tệp đã lưu là khác nhau.

Các tệp nhỏ hơn như Lịch trình.html, được gửi gần như cuối cùng đã được tải xuống sớm hơn (ví dụ: ít byte hơn để tải xuống) và lần lượt được lưu vào các tệp cục bộ sớm hơn.sched.html” that were dispatched nearly last were downloaded sooner (e.g. less bytes to download) and in turn were saved to local files sooner.

Điều này xác nhận rằng chúng tôi thực sự đang xử lý các bản tải xuống theo thứ tự hoàn thành nhiệm vụ của họ và không phải là thứ tự các nhiệm vụ đã được gửi.

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library/queue.html to Docs/queue.html

> Đã lưu https://docs.python.org/3/l Library

> Đã lưu https://docs.python.org/3/l Library/Threading.html to Docs/Threading.html

> Đã lưu https://docs.python.org/3/l Library/concivers

> Đã lưu https://docs.python.org/3/l Library/SubProcess.html to Docs/Subprocess.html

> Đã lưu https://docs.python.org/3/l Library

Bây giờ chúng ta đã thấy một số ví dụ, hãy để xem xét một số mẫu sử dụng phổ biến khi sử dụng ThreadPoolExecutor.ThreadPoolExecutor.

Các mẫu sử dụng ThreadPoolExecutor

ThreadPoolExecutor cung cấp rất nhiều sự linh hoạt để thực hiện các nhiệm vụ đồng thời trong Python.ThreadPoolExecutor provides a lot of flexibility for executing concurrent tasks in Python.

Tuy nhiên, có một số ít các mẫu sử dụng phổ biến sẽ phù hợp với hầu hết các kịch bản chương trình.

Phần này liệt kê các mẫu sử dụng phổ biến với các ví dụ đã làm việc mà bạn có thể sao chép và dán vào dự án của riêng bạn và thích ứng khi cần thiết.

Các mẫu chúng ta sẽ xem xét như sau:

  • Bản đồ và mô hình chờ
  • Gửi và sử dụng làm mẫu đã hoàn thành
  • Gửi và sử dụng mẫu tuần tự
  • Gửi và sử dụng mẫu gọi lại
  • Gửi và chờ tất cả các mẫu
  • Gửi và đợi mẫu đầu tiên

Chúng tôi sẽ sử dụng một nhiệm vụ giả định trong mỗi ví dụ sẽ ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây. Bạn có thể dễ dàng thay thế nhiệm vụ ví dụ này bằng nhiệm vụ của riêng bạn trong mỗi mẫu.

Ngoài ra, hãy nhớ lại rằng mỗi chương trình Python có một luồng theo mặc định được gọi là luồng chính nơi chúng tôi thực hiện công việc của chúng tôi. Chúng tôi sẽ tạo nhóm luồng trong luồng chính trong mỗi ví dụ và có thể tham chiếu các hành động trong luồng chính trong một số mẫu, trái ngược với các hành động trong các luồng trong nhóm luồng.

Bản đồ và mô hình chờ

Gửi và sử dụng làm mẫu đã hoàn thànhThreadPoolExecutor is to convert a for loop that executes a function on each item in a collection to use threads.

Gửi và sử dụng mẫu tuần tự

Gửi và sử dụng mẫu gọi lại

Gửi và chờ tất cả các mẫu..

Gửi và đợi mẫu đầu tiên

Chúng tôi sẽ sử dụng một nhiệm vụ giả định trong mỗi ví dụ sẽ ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây. Bạn có thể dễ dàng thay thế nhiệm vụ ví dụ này bằng nhiệm vụ của riêng bạn trong mỗi mẫu.item inmylist:

result=task(item)=task(item)

Ngoài ra, hãy nhớ lại rằng mỗi chương trình Python có một luồng theo mặc định được gọi là luồng chính nơi chúng tôi thực hiện công việc của chúng tôi. Chúng tôi sẽ tạo nhóm luồng trong luồng chính trong mỗi ví dụ và có thể tham chiếu các hành động trong luồng chính trong một số mẫu, trái ngược với các hành động trong các luồng trong nhóm luồng.map() function that applies the function to each item in the iterable for you.

Gửi và chờ tất cả các mẫu..

Gửi và đợi mẫu đầu tiên

results=map(task,mylist)=map(task,mylist)

Chúng tôi sẽ sử dụng một nhiệm vụ giả định trong mỗi ví dụ sẽ ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây. Bạn có thể dễ dàng thay thế nhiệm vụ ví dụ này bằng nhiệm vụ của riêng bạn trong mỗi mẫu.task() function to each item until we iterate the results, so-called lazy evaluation:

Gửi và chờ tất cả các mẫu..

Gửi và đợi mẫu đầu tiên

Chúng tôi sẽ sử dụng một nhiệm vụ giả định trong mỗi ví dụ sẽ ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây. Bạn có thể dễ dàng thay thế nhiệm vụ ví dụ này bằng nhiệm vụ của riêng bạn trong mỗi mẫu.result inresults:

print(result)(result)

Ngoài ra, hãy nhớ lại rằng mỗi chương trình Python có một luồng theo mặc định được gọi là luồng chính nơi chúng tôi thực hiện công việc của chúng tôi. Chúng tôi sẽ tạo nhóm luồng trong luồng chính trong mỗi ví dụ và có thể tham chiếu các hành động trong luồng chính trong một số mẫu, trái ngược với các hành động trong các luồng trong nhóm luồng.

Gửi và chờ tất cả các mẫu..

Gửi và đợi mẫu đầu tiên

Chúng tôi sẽ sử dụng một nhiệm vụ giả định trong mỗi ví dụ sẽ ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây. Bạn có thể dễ dàng thay thế nhiệm vụ ví dụ này bằng nhiệm vụ của riêng bạn trong mỗi mẫu.result inmap(task,mylist):

print(result)(result)

Ngoài ra, hãy nhớ lại rằng mỗi chương trình Python có một luồng theo mặc định được gọi là luồng chính nơi chúng tôi thực hiện công việc của chúng tôi. Chúng tôi sẽ tạo nhóm luồng trong luồng chính trong mỗi ví dụ và có thể tham chiếu các hành động trong luồng chính trong một số mẫu, trái ngược với các hành động trong các luồng trong nhóm luồng.

Gửi và chờ tất cả các mẫu..

Gửi và đợi mẫu đầu tiên

Chúng tôi sẽ sử dụng một nhiệm vụ giả định trong mỗi ví dụ sẽ ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây. Bạn có thể dễ dàng thay thế nhiệm vụ ví dụ này bằng nhiệm vụ của riêng bạn trong mỗi mẫu.result inexecutor.map(task,mylist):

print(result)(result)

Ngoài ra, hãy nhớ lại rằng mỗi chương trình Python có một luồng theo mặc định được gọi là luồng chính nơi chúng tôi thực hiện công việc của chúng tôi. Chúng tôi sẽ tạo nhóm luồng trong luồng chính trong mỗi ví dụ và có thể tham chiếu các hành động trong luồng chính trong một số mẫu, trái ngược với các hành động trong các luồng trong nhóm luồng.map() function.

Có lẽ mẫu phổ biến nhất khi sử dụng ThreadPoolExecutor là chuyển đổi vòng lặp For thực thi hàm trên mỗi mục trong một bộ sưu tập để sử dụng các luồng.map() as a concurrent version of the map() function and is ideal if you are looking to update your for loop to use threads.

Nó giả định rằng chức năng không có tác dụng phụ, có nghĩa là nó không truy cập bất kỳ dữ liệu nào bên ngoài hàm và không thay đổi dữ liệu được cung cấp cho nó. Nó lấy dữ liệu và tạo ra một kết quả.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

Những loại vòng lặp này có thể được viết rõ ràng bằng Python; Ví dụ:

...

# Áp dụng chức năng cho từng phần tử trong bộ sưu tậptime import sleep

Foritem Inmylist:random import random

Một thực tiễn tốt hơn là sử dụng chức năng Bản đồ tích hợp () áp dụng hàm cho từng mục trong điều kiện có thể cho bạn.concurrent.futures import ThreadPoolExecutor

# Áp dụng chức năng cho từng phần tử trong bộ sưu tập

Điều này không thực hiện hàm nhiệm vụ () cho từng mục cho đến khi chúng tôi lặp lại kết quả, được gọi là đánh giá lười biếng:task(name):

# lặp lại kết quả từ bản đồ# sleep for less than a second

    sleep(random())sleep(random())

    returnnamereturnname

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (10) Asexecutor:ThreadPoolExecutor(10)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực thi các nhiệm vụ đồng thời và xử lý kết quả theo thứ tự# execute tasks concurrently and process results in order

& nbsp; & nbsp; & nbsp; & nbsp;forresult inexecutor.map(task, range(10)):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# retrieve the result

        print(result)print(result)

Chạy ví dụ, chúng ta có thể thấy rằng các kết quả được báo cáo theo thứ tự các tác vụ được tạo và gửi vào nhóm luồng.

Hàm bản đồ () hỗ trợ các hàm mục tiêu lấy nhiều hơn một đối số bằng cách cung cấp nhiều hơn các đối số khác như đối số cho Call to Map ().map() function supports target functions that take more than one argument by providing more than iterable as arguments to the call to map().

Ví dụ: chúng ta có thể xác định chức năng đích cho bản đồ có hai đối số, sau đó cung cấp hai lần lặp có cùng độ dài cho cuộc gọi đến bản đồ.

Ví dụ hoàn chỉnh được liệt kê dưới đây.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

# SuperfastPython.com

# Ví dụ về bản đồ gọi với hai lần lặp

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

nhiệm vụ def (value1, value2):task(value1,value2):

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# sleep for less than a second

    sleep(random())sleep(random())

    return(value1,value2)return(value1, value2)

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi tất cả các nhiệm vụ# submit all tasks

& nbsp; & nbsp; & nbsp; & nbsp;forresult in executor.map(task,['1','2','3'],['a','b','c']):

        print(result)print(result)

Chạy ví dụ thực hiện các tác vụ như mong đợi, cung cấp hai đối số để lập bản đồ và báo cáo kết quả kết hợp cả hai đối số.

('1', 'A')

('2', 'B')

('3', 'C')

Một cuộc gọi đến chức năng MAP sẽ phát hành tất cả các tác vụ cho nhóm luồng ngay lập tức, ngay cả khi bạn không lặp lại kết quả có thể lặp lại.

Điều này không giống như hàm Bản đồ tích hợp () lười biếng và không tính toán từng cuộc gọi cho đến khi bạn yêu cầu kết quả trong quá trình lặp.map() function that is lazy and does not compute each call until you ask for the result during iteration.

Ví dụ dưới đây xác nhận điều này bằng cách ban hành tất cả các tác vụ bằng bản đồ và không lặp lại kết quả.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

# SuperfastPython.com

# Ví dụ về bản đồ gọi với hai lần lặp

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

nhiệm vụ def (value1, value2):task(value):

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# sleep for less than a second

    sleep(random())sleep(random())

với ThreadPoolExecutor () Asexecutor:print(f'Done: {value}')

    returnvaluereturn value

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi tất cả các nhiệm vụ# submit all tasks

    executor.map(task,range(5))executor.map(task, range(5))

& nbsp; & nbsp; & nbsp; & nbsp;('All done!')

Chạy ví dụ thực hiện các tác vụ như mong đợi, cung cấp hai đối số để lập bản đồ và báo cáo kết quả kết hợp cả hai đối số.

('1', 'A')

('2', 'B')

('3', 'C')

Một cuộc gọi đến chức năng MAP sẽ phát hành tất cả các tác vụ cho nhóm luồng ngay lập tức, ngay cả khi bạn không lặp lại kết quả có thể lặp lại.

Điều này không giống như hàm Bản đồ tích hợp () lười biếng và không tính toán từng cuộc gọi cho đến khi bạn yêu cầu kết quả trong quá trình lặp.

Ví dụ dưới đây xác nhận điều này bằng cách ban hành tất cả các tác vụ bằng bản đồ và không lặp lại kết quả.

# Ví dụ về việc gọi bản đồ và không lặp lại kết quả

nhiệm vụ def (giá trị):

& nbsp; & nbsp; & nbsp; & nbsp; print (f'done: {value} '))ThreadPoolExecutor is to submit tasks and use the results as they become available.

In ('Tất cả đã hoàn thành!')submit() function to push tasks into the thread pool that returns Future objects, then calling the module method as_completed() on the list of Future objects that will return each Future object as it’s task is completed.

Chạy ví dụ, chúng ta có thể thấy rằng các tác vụ được gửi vào nhóm luồng và được thực thi mà không phải vượt qua rõ ràng các kết quả được trả về.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# SuperfastPython.com

# Ví dụ về bản đồ gọi với hai lần lặp

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổiconcurrent.futures import as_completed

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

nhiệm vụ def (value1, value2):task(name):

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# sleep for less than a second

    sleep(random())sleep(random())

    returnnamereturnname

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (10) Asexecutor:ThreadPoolExecutor(10)asexecutor:

với ThreadPoolExecutor () Asexecutor:# submit tasks and collect futures

    futures=[executor.submit(task,i)foriinrange(10)]futures= [executor.submit(task,i)foriinrange(10)]

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi tất cả các nhiệm vụ# process task results as they are available

& nbsp; & nbsp; & nbsp; & nbsp;for future inas_completed(futures):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# retrieve the result

        print(future.result())print(future.result())

Chạy ví dụ, chúng ta có thể thấy rằng các kết quả được báo cáo theo thứ tự các tác vụ được tạo và gửi vào nhóm luồng.

Gửi và sử dụng tuần tự

Chúng tôi có thể yêu cầu kết quả từ các nhiệm vụ theo thứ tự các nhiệm vụ đã được gửi.

Điều này có thể là do các nhiệm vụ có trật tự tự nhiên.

Chúng tôi có thể thực hiện mẫu này bằng cách gọi Substem () cho mỗi nhiệm vụ để có được một danh sách các đối tượng trong tương lai, sau đó lặp lại các đối tượng trong tương lai theo thứ tự các tác vụ đã được gửi và truy xuất kết quả.submit() for each task to get a list of Future objects, then iterating over the Future objects in the order that the tasks were submitted and retrieving the results.

Sự khác biệt chính so với mẫu AS AS AS đã hoàn thành là chúng tôi liệt kê trực tiếp danh sách các tương lai, thay vì gọi hàm as_completed ().as_completed() function.

.....

# Quy trình kết quả nhiệm vụ theo thứ tự họ đã được gửi

Infutures forfuture:future infutures:

# Lấy kết quả

print(future.result())(future.result())

Ví dụ dưới đây cho thấy mẫu này, gửi các nhiệm vụ theo thứ tự từ 0 đến 9 và hiển thị kết quả theo thứ tự mà chúng đã được gửi.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

# SuperfastPython.com

# Ví dụ về việc gửi và sử dụng mẫu tuần tự cho ThreadPoolExecutor

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

nhiệm vụ def (tên):task(name):

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# sleep for less than a second

    sleep(random())sleep(random())

    returnnamereturnname

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (10) Asexecutor:ThreadPoolExecutor(10)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi các nhiệm vụ và thu thập tương lai# submit tasks and collect futures

    futures=[executor.submit(task,i)foriinrange(10)]futures=[executor.submit(task, i)foriinrange(10)]

& nbsp; & nbsp; & nbsp; & nbsp;# kết quả nhiệm vụ quy trình theo thứ tự họ đã được gửi# process task results in the order they were submitted

& nbsp; & nbsp; & nbsp; & nbsp; forfuture Infutures:forfuture infutures:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# retrieve the result

        print(future.result())print(future.result())

Chạy ví dụ, chúng ta có thể thấy rằng các kết quả được truy xuất và in theo thứ tự các tác vụ đã được gửi, chứ không phải thứ tự mà các tác vụ đã được hoàn thành.

Gửi và sử dụng gọi lại

Chúng tôi có thể không muốn xử lý rõ ràng kết quả khi chúng có sẵn; Thay vào đó, chúng tôi muốn gọi một chức năng trên kết quả.

Thay vì thực hiện điều này theo cách thủ công, chẳng hạn như trong mẫu đã hoàn thành ở trên, chúng ta có thể tự động gọi nhóm luồng cho chúng ta với kết quả.

Điều này có thể đạt được bằng cách đặt một cuộc gọi lại trên mỗi đối tượng trong tương lai bằng cách gọi hàm add_done_callback () và truyền tên của hàm.Future object by calling the add_done_callback() function and passing the name of the function.

Sau đó, nhóm luồng sẽ gọi chức năng gọi lại khi mỗi tác vụ hoàn thành, chuyển trong các đối tượng trong tương lai cho tác vụ.Future objects for the task.

Ví dụ dưới đây cho thấy mẫu này, đăng ký chức năng gọi lại tùy chỉnh sẽ được áp dụng cho mỗi tác vụ khi hoàn thành.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

# SuperfastPython.com

# Ví dụ về việc gửi và sử dụng mẫu tuần tự cho ThreadPoolExecutor

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

nhiệm vụ def (tên):task(name):

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# sleep for less than a second

    sleep(random())sleep(random())

    returnnamereturnname

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (10) Asexecutor:custom_callback(fut):

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi các nhiệm vụ và thu thập tương lai# retrieve the result

    print(fut.result())print(fut.result())

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (10) Asexecutor:ThreadPoolExecutor(10)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi các nhiệm vụ và thu thập tương lai# submit tasks and collect futures

    futures=[executor.submit(task,i)foriinrange(10)]futures=[executor.submit(task, i)foriinrange(10)]

& nbsp; & nbsp; & nbsp; & nbsp;# kết quả nhiệm vụ quy trình theo thứ tự họ đã được gửi# register the callback on all tasks

& nbsp; & nbsp; & nbsp; & nbsp; forfuture Infutures:forfuture infutures:

        future.add_done_callback(custom_callback)future.add_done_callback(custom_callback)

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# wait for tasks to complete...

Chạy ví dụ, chúng ta có thể thấy rằng các kết quả được truy xuất và in theo thứ tự các tác vụ đã được gửi, chứ không phải thứ tự mà các tác vụ đã được hoàn thành.

Gửi và sử dụng gọi lạiFuture object; it is not limited to a single callback.

Chúng tôi có thể không muốn xử lý rõ ràng kết quả khi chúng có sẵn; Thay vào đó, chúng tôi muốn gọi một chức năng trên kết quả.Future object.

Thay vì thực hiện điều này theo cách thủ công, chẳng hạn như trong mẫu đã hoàn thành ở trên, chúng ta có thể tự động gọi nhóm luồng cho chúng ta với kết quả.Future.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

# SuperfastPython.com

# Ví dụ về việc gửi và sử dụng mẫu tuần tự cho ThreadPoolExecutor

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

nhiệm vụ def (tên):task(name):

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# sleep for less than a second

    sleep(random())sleep(random())

    returnnamereturnname

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (10) Asexecutor:custom_callback1(fut):

& nbsp; & nbsp; & nbsp; & nbsp;# truy xuất kết quả# retrieve the result

& nbsp; & nbsp; & nbsp; & nbsp; print (f'callback 1: {fut.result ()} ')print(f'Callback 1: {fut.result()}')

# chức năng gọi lại tùy chỉnh được gọi trên các tác vụ khi chúng hoàn thành

def Custom_callback2 (FUT):custom_callback2(fut):

& nbsp; & nbsp; & nbsp; & nbsp;# truy xuất kết quả# retrieve the result

& nbsp; & nbsp; & nbsp; & nbsp; print (f'callback 2: {fut.result ()} ')print(f'Callback 2: {fut.result()}')

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (10) Asexecutor:ThreadPoolExecutor(10)as executor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi các nhiệm vụ và thu thập tương lai# submit tasks and collect futures

    futures=[executor.submit(task,i)foriinrange(10)]futures=[executor.submit(task,i)foriin range(10)]

& nbsp; & nbsp; & nbsp; & nbsp;# đăng ký các cuộc gọi lại trên tất cả các tác vụ# register the callbacks on all tasks

& nbsp; & nbsp; & nbsp; & nbsp; forfuture Infutures:forfuture infutures:

        future.add_done_callback(custom_callback1)future.add_done_callback(custom_callback1)

        future.add_done_callback(custom_callback2)future.add_done_callback(custom_callback2)

& nbsp; & nbsp; & nbsp; & nbsp;# chờ hoàn thành các nhiệm vụ ...# wait for tasks to complete...

Chạy ví dụ, chúng ta có thể thấy rằng kết quả được báo cáo theo thứ tự các tác vụ đã được hoàn thành và hai chức năng gọi lại được gọi cho mỗi nhiệm vụ theo thứ tự chúng tôi đã đăng ký chúng với mỗi đối tượng trong tương lai.Future object.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

Callback 1: 3

Gọi lại 2: 3

Callback 1: 9

Gọi lại 2: 9

Callback 1: 7

Callback 2: 7

Callback 1: 2

Gọi lại 2: 2

Callback 1: 0

Gọi lại 2: 0

Callback 1: 5

Gọi lại 2: 5

Callback 1: 1

Gọi lại 2: 1

Callback 1: 8

Gọi lại 2: 8

Callback 1: 4

Gọi lại 2: 4

Callback 1: 6

Gọi lại 2: 6

Gửi và chờ tất cả

Người ta thường gửi tất cả các nhiệm vụ và sau đó chờ tất cả các tác vụ trong nhóm luồng hoàn thành.

Mẫu này có thể hữu ích khi các tác vụ không trả về kết quả trực tiếp, chẳng hạn như nếu mỗi tác vụ lưu trữ kết quả trong một tài nguyên trực tiếp giống như một tệp.

Có hai cách mà chúng ta có thể chờ đợi các tác vụ hoàn thành: bằng cách gọi hàm mô -đun chờ () hoặc bằng cách gọi tắt ().wait() module function or by calling shutdown().

Trường hợp có khả năng nhất là bạn muốn chờ đợi rõ ràng một bộ hoặc tập hợp con của các tác vụ trong nhóm luồng để hoàn thành.

Bạn có thể đạt được điều này bằng cách chuyển danh sách các tác vụ cho hàm Wait (), theo mặc định, sẽ chờ tất cả các tác vụ hoàn tất.wait() function, which, by default, will wait for all tasks to complete.

.....

# Đợi tất cả các nhiệm vụ hoàn thành

wait(futures)(futures)

Chúng ta có thể chỉ định rõ ràng để chờ tất cả các tác vụ bằng cách đặt đối số của return return_when, thành hằng số all_completed; Ví dụ:return_when” argument to the ALL_COMPLETED constant; for example:

.....

# Đợi tất cả các nhiệm vụ hoàn thành

wait(futures,return_when=ALL_COMPLETED)(futures,return_when=ALL_COMPLETED)

Chúng ta có thể chỉ định rõ ràng để chờ tất cả các tác vụ bằng cách đặt đối số của return return_when, thành hằng số all_completed; Ví dụ:wait() as we have no need to inspect it in this case.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

Ví dụ dưới đây cho thấy mô hình này. Lưu ý rằng chúng tôi đang cố tình bỏ qua việc trả lại từ Gọi Wait () vì chúng tôi không cần phải kiểm tra nó trong trường hợp này.

# SuperfastPython.com

# Ví dụ về việc gửi và chờ tất cả các mẫu cho ThreadPoolExecutortime import sleep

từ thời gian nhập vào giấc ngủrandom import random

từ nhập ngẫu nhiên ngẫu nhiênconcurrent.futures import ThreadPoolExecutor

từ đồng thời.concurrent.futures import wait

từ đồng thời.

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổitask(name):

nhiệm vụ def (tên):# sleep for less than a second

    sleep(random())sleep(random())

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# display the result

    print(name)print(name)

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (10) Asexecutor:ThreadPoolExecutor(10)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi các nhiệm vụ và thu thập tương lai# submit tasks and collect futures

    futures=[executor.submit(task,i)foriinrange(10)]futures =[executor.submit(task,i)foriinrange(10)]

& nbsp; & nbsp; & nbsp; & nbsp;# đăng ký các cuộc gọi lại trên tất cả các tác vụ# wait for all tasks to complete

    wait(futures)wait(futures)

& nbsp; & nbsp; & nbsp; & nbsp; forfuture Infutures:print('All tasks are done!')

& nbsp; & nbsp; & nbsp; & nbsp;# chờ hoàn thành các nhiệm vụ ...

3

9

0

8

4

6

2

1

5

7

Chạy ví dụ, chúng ta có thể thấy rằng kết quả được báo cáo theo thứ tự các tác vụ đã được hoàn thành và hai chức năng gọi lại được gọi cho mỗi nhiệm vụ theo thứ tự chúng tôi đã đăng ký chúng với mỗi đối tượng trong tương lai.

Callback 1: 3

Gọi lại 2: 3Future objects or when we only intend to use the thread pool once for a set of tasks.

Callback 1: 9

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

Ví dụ dưới đây cho thấy mô hình này. Lưu ý rằng chúng tôi đang cố tình bỏ qua việc trả lại từ Gọi Wait () vì chúng tôi không cần phải kiểm tra nó trong trường hợp này.

# SuperfastPython.com

# Ví dụ về việc gửi và chờ tất cả các mẫu cho ThreadPoolExecutortime import sleep

từ thời gian nhập vào giấc ngủrandom import random

từ nhập ngẫu nhiên ngẫu nhiênconcurrent.futures import ThreadPoolExecutor

từ đồng thời.

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổitask(name):

nhiệm vụ def (tên):# sleep for less than a second

    sleep(random())sleep(random())

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# display the result

    print(name)print(name)

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (10) Asexecutor:ThreadPoolExecutor(10)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi các nhiệm vụ và thu thập tương lai# submit tasks and collect futures

    futures=[executor.submit(task,i)foriinrange(10)]futures= [executor.submit(task,i)foriinrange(10)]

& nbsp; & nbsp; & nbsp; & nbsp;# đăng ký các cuộc gọi lại trên tất cả các tác vụ# wait for all tasks to complete

In ('Tất cả các nhiệm vụ đã được thực hiện!')('All tasks are done!')

Chạy ví dụ, chúng ta có thể thấy rằng luồng chính không di chuyển và in tin nhắn cho đến khi tất cả các tác vụ được hoàn thành, sau khi nhóm luồng đã được trình quản lý ngữ cảnh tự động tắt.

1

2

8

4

5

3

9

0

7

6

Tất cả các nhiệm vụ được thực hiện!

Mẫu tắt tự động của Trình quản lý bối cảnh có thể gây nhầm lẫn cho các nhà phát triển không được sử dụng với cách các nhóm luồng hoạt động, do đó nhận xét ở cuối khối Trình quản lý bối cảnh trong ví dụ trước.

Chúng ta có thể đạt được hiệu ứng tương tự mà không cần người quản lý ngữ cảnh và một cuộc gọi rõ ràng để tắt máy.

.....

# Đợi tất cả các nhiệm vụ hoàn thành và đóng hồ bơi

executor.shutdown().shutdown()

Hãy nhớ lại rằng hàm Shutdown () sẽ chờ tất cả các tác vụ hoàn thành theo mặc định và sẽ không hủy bất kỳ tác vụ được xếp hàng nào, nhưng chúng ta có thể đưa ra điều này rõ ràng bằng cách đặt đối số chờ đợi của Wait thành True và đối số của Hủy hủy_futures thành sai; Ví dụ:shutdown() function will wait for all tasks to complete by default and will not cancel any queued tasks, but we can make this explicit by setting the “wait” argument to True and the “cancel_futures” argument to False; for example:

.....

# Đợi tất cả các nhiệm vụ hoàn thành và đóng hồ bơi

executor.shutdown(wait=True,cancel_futures=False).shutdown(wait=True,cancel_futures=False)

Hãy nhớ lại rằng hàm Shutdown () sẽ chờ tất cả các tác vụ hoàn thành theo mặc định và sẽ không hủy bất kỳ tác vụ được xếp hàng nào, nhưng chúng ta có thể đưa ra điều này rõ ràng bằng cách đặt đối số chờ đợi của Wait thành True và đối số của Hủy hủy_futures thành sai; Ví dụ:shutdown() before moving on.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

Ví dụ dưới đây cho thấy mô hình chờ đợi tất cả các tác vụ trong nhóm luồng hoàn thành bằng cách gọi tắt () trước khi tiếp tục.

# SuperfastPython.com

# Ví dụ về việc gửi và chờ tất cả với mẫu tắt cho ThreadPoolExecutortime import sleep

từ thời gian nhập vào giấc ngủrandom import random

từ nhập ngẫu nhiên ngẫu nhiênconcurrent.futures import ThreadPoolExecutor

từ đồng thời.

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổitask(name):

nhiệm vụ def (tên):# sleep for less than a second

    sleep(random())sleep(random())

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# display the result

    print(name)print(name)

& nbsp; & nbsp; & nbsp; & nbsp;# hiển thị kết quả

executor=ThreadPoolExecutor(10)=ThreadPoolExecutor(10)

# Bắt đầu nhóm chủ đề

futures=[executor.submit(task,i)foriinrange(10)]=[executor.submit(task,i)for iinrange(10)]

# Gửi nhiệm vụ và thu thập tương lai

executor.shutdown().shutdown()

In ('Tất cả các nhiệm vụ đã được thực hiện!')('All tasks are done!')

# Đợi tất cả các nhiệm vụ hoàn thành

3

5

2

6

8

9

7

1

4

0

Tất cả các nhiệm vụ được thực hiện!

Mẫu tắt tự động của Trình quản lý bối cảnh có thể gây nhầm lẫn cho các nhà phát triển không được sử dụng với cách các nhóm luồng hoạt động, do đó nhận xét ở cuối khối Trình quản lý bối cảnh trong ví dụ trước.

Chúng ta có thể đạt được hiệu ứng tương tự mà không cần người quản lý ngữ cảnh và một cuộc gọi rõ ràng để tắt máy.

...

# Đợi tất cả các nhiệm vụ hoàn thành và đóng hồ bơi

Hãy nhớ lại rằng hàm Shutdown () sẽ chờ tất cả các tác vụ hoàn thành theo mặc định và sẽ không hủy bất kỳ tác vụ được xếp hàng nào, nhưng chúng ta có thể đưa ra điều này rõ ràng bằng cách đặt đối số chờ đợi của Wait thành True và đối số của Hủy hủy_futures thành sai; Ví dụ:wait() module function and setting the “return_when” argument to the FIRST_COMPLETED constant.

.....

# Đợi tất cả các nhiệm vụ hoàn thành và đóng hồ bơi

done,not_done=wait(futures,return_when=FIRST_COMPLETED),not_done=wait(futures,return_when=FIRST_COMPLETED)

Hãy nhớ lại rằng hàm Shutdown () sẽ chờ tất cả các tác vụ hoàn thành theo mặc định và sẽ không hủy bất kỳ tác vụ được xếp hàng nào, nhưng chúng ta có thể đưa ra điều này rõ ràng bằng cách đặt đối số chờ đợi của Wait thành True và đối số của Hủy hủy_futures thành sai; Ví dụ:shutdown() manually so that we can continue on with the execution of the main thread without waiting for all of the other tasks to complete.

Ví dụ dưới đây cho thấy mô hình chờ đợi tất cả các tác vụ trong nhóm luồng hoàn thành bằng cách gọi tắt () trước khi tiếp tục.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

Ví dụ dưới đây cho thấy mô hình chờ đợi tất cả các tác vụ trong nhóm luồng hoàn thành bằng cách gọi tắt () trước khi tiếp tục.

# SuperfastPython.com

# Ví dụ về việc gửi và chờ tất cả với mẫu tắt cho ThreadPoolExecutortime import sleep

từ thời gian nhập vào giấc ngủrandom import random

từ nhập ngẫu nhiên ngẫu nhiênconcurrent.futures import ThreadPoolExecutor

từ đồng thời.concurrent.futures import wait

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổiconcurrent.futures import FIRST_COMPLETED

từ đồng thời.

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổitask(name):

nhiệm vụ def (tên):# sleep for less than a second

    sleep(random())sleep(random())

    returnnamereturnname

& nbsp; & nbsp; & nbsp; & nbsp;# hiển thị kết quả

executor=ThreadPoolExecutor(10)=ThreadPoolExecutor(10)

# Bắt đầu nhóm chủ đề

futures=[executor.submit(task,i)foriinrange(10)] =[executor.submit(task,i)foriinrange(10)]

# Đợi tất cả các nhiệm vụ hoàn thành và đóng hồ bơi

done,not_done=wait(futures,return_when=FIRST_COMPLETED),not_done =wait(futures,return_when=FIRST_COMPLETED)

Hãy nhớ lại rằng hàm Shutdown () sẽ chờ tất cả các tác vụ hoàn thành theo mặc định và sẽ không hủy bất kỳ tác vụ được xếp hàng nào, nhưng chúng ta có thể đưa ra điều này rõ ràng bằng cách đặt đối số chờ đợi của Wait thành True và đối số của Hủy hủy_futures thành sai; Ví dụ:

print(done.pop().result())(done.pop().result())

Ví dụ dưới đây cho thấy mô hình chờ đợi tất cả các tác vụ trong nhóm luồng hoàn thành bằng cách gọi tắt () trước khi tiếp tục.

executor.shutdown(wait=False,cancel_futures=True).shutdown(wait=False,cancel_futures=True)

# SuperfastPython.com

# Ví dụ về việc gửi và chờ tất cả với mẫu tắt cho ThreadPoolExecutor

từ thời gian nhập vào giấc ngủThreadPoolExecutor, let’s look at how we might customize the configuration of the thread pool.

từ nhập ngẫu nhiên ngẫu nhiên

từ đồng thời.ThreadPoolExecutor instance.

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

Hãy cùng nhau xem xét kỹ hơn từng cái.

Định cấu hình số lượng luồng

Số lượng luồng trong nhóm luồng có thể được cấu hình bởi đối số Max Max_Workers.max_workers” argument.

Nó có một số nguyên dương và mặc định cho số lượng CPU trong hệ thống của bạn cộng với bốn.

  • Tổng số nhân công chủ đề = (CPU trong hệ thống của bạn) + 4

Ví dụ: nếu bạn có 2 CPU vật lý trong hệ thống của mình và mỗi CPU có hyperthreading (phổ biến trong CPU hiện đại) thì bạn sẽ có 2 CPU vật lý và 4 CPU logic. Python sẽ thấy 4 CPU. Số lượng các luồng công nhân mặc định trên hệ thống của bạn sau đó sẽ là (4 + 4) hoặc 8.

Nếu số này xuất hiện là hơn 32 (ví dụ: 16 lõi vật lý, 32 lõi logic, cộng với bốn), mặc định sẽ cắt các luồng trên xuống 32.

Người ta thường có nhiều luồng hơn CPU (vật lý hoặc logic) trong hệ thống của bạn.

Lý do cho điều này là do các chủ đề được sử dụng cho các nhiệm vụ gắn IO, không phải là các nhiệm vụ ràng buộc CPU. Điều này có nghĩa là các chủ đề được sử dụng cho các tác vụ chờ đợi các tài nguyên tương đối chậm để trả lời, như ổ cứng, ổ đĩa DVD, máy in và kết nối mạng, và nhiều hơn nữa. Chúng tôi sẽ thảo luận về ứng dụng tốt nhất của các chủ đề trong phần sau.

Do đó, không có gì lạ khi có hàng chục, hàng trăm và thậm chí hàng ngàn chủ đề trong ứng dụng của bạn, tùy thuộc vào nhu cầu cụ thể của bạn. Thật bất thường khi có nhiều hơn một hoặc vài nghìn chủ đề. Nếu bạn yêu cầu nhiều chủ đề này, thì các giải pháp thay thế có thể được ưu tiên, chẳng hạn như asyncio. Chúng tôi sẽ thảo luận về các chủ đề so với Asyncio trong phần sau.

Đầu tiên, hãy để kiểm tra số lượng chủ đề được tạo cho các nhóm chủ đề trên hệ thống của bạn.

Nhìn vào mã nguồn cho ThreadPoolExecutor, chúng ta có thể thấy rằng số lượng chủ đề công nhân được chọn theo mặc định được lưu trữ trong thuộc tính _max_workers, chúng ta có thể truy cập và báo cáo sau khi một nhóm luồng được tạo.ThreadPoolExecutor, we can see that the number of worker threads chosen by default is stored in the _max_workers property, which we can access and report after a thread pool is created.

Lưu ý: Hồi _Max_Workers là một thành viên được bảo vệ và có thể thay đổi trong tương lai._max_workers” is a protected member and may change in the future.

Ví dụ dưới đây báo cáo số lượng luồng mặc định trong nhóm luồng trên hệ thống của bạn.

# SuperfastPython.com

# Báo cáo số lượng chủ đề công nhân mặc định trên hệ thống của bạn

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Tạo một nhóm luồng với số lượng chủ đề công nhân mặc định

pool=ThreadPoolExecutor()=ThreadPoolExecutor()

# Báo cáo số lượng chủ đề nhân viên được chọn theo mặc định

print(pool._max_workers)(pool._max_workers)

Chạy ví dụ báo cáo số lượng chủ đề công nhân được sử dụng theo mặc định trên hệ thống của bạn.

Tôi có bốn lõi CPU vật lý và tám lõi logic; Do đó, mặc định là 8 + 4 hoặc 12 luồng.

Có bao nhiêu luồng công nhân được phân bổ theo mặc định trên hệ thống của bạn? Hãy cho tôi biết trong các bình luận bên dưới.
Let me know in the comments below.

Chúng tôi có thể chỉ định số lượng chủ đề công nhân trực tiếp và đây là một ý tưởng tốt trong hầu hết các ứng dụng.

Ví dụ dưới đây trình bày cách định cấu hình 500 luồng công nhân.

# SuperfastPython.com

# Báo cáo số lượng chủ đề công nhân mặc định trên hệ thống của bạn

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Tạo một nhóm luồng với số lượng chủ đề công nhân mặc định

pool=ThreadPoolExecutor(500)=ThreadPoolExecutor(500)

# Báo cáo số lượng chủ đề nhân viên được chọn theo mặc định

print(pool._max_workers)(pool._max_workers)

Chạy ví dụ báo cáo số lượng chủ đề công nhân được sử dụng theo mặc định trên hệ thống của bạn.

Tôi có bốn lõi CPU vật lý và tám lõi logic; Do đó, mặc định là 8 + 4 hoặc 12 luồng.

Có bao nhiêu luồng công nhân được phân bổ theo mặc định trên hệ thống của bạn? Hãy cho tôi biết trong các bình luận bên dưới.

Chúng tôi có thể chỉ định số lượng chủ đề công nhân trực tiếp và đây là một ý tưởng tốt trong hầu hết các ứng dụng.

Ví dụ dưới đây trình bày cách định cấu hình 500 luồng công nhân.

# Định cấu hình và báo cáo số lượng chủ đề công nhân mặc định

# Tạo một nhóm chủ đề với số lượng lớn các chủ đề công nhân

# Báo cáo số lượng chủ đề công nhânMainThread“. You can access the main thread via a call to the main_thread() function in the threading module and then access the name member. For example:

Chạy ví dụ định cấu hình nhóm luồng để sử dụng 500 luồng và xác nhận rằng nó sẽ tạo 500 luồng.

Bạn nên sử dụng bao nhiêu chủ đề?threading import main_thread

Nếu bạn có hàng trăm tác vụ, có lẽ bạn nên đặt số lượng luồng bằng số lượng tác vụ.

thread=main_thread()=main_thread()

Nếu bạn có hàng ngàn nhiệm vụ, có lẽ bạn nên giới hạn số lượng chủ đề ở hàng trăm hoặc 1.000.

print(thread.name)(thread.name)

Nếu ứng dụng của bạn được dự định sẽ được thực hiện nhiều lần trong tương lai, bạn có thể kiểm tra các số luồng khác nhau và so sánh thời gian thực hiện tổng thể, sau đó chọn một số luồng mang lại hiệu suất gần như tốt nhất. Bạn có thể muốn chế giễu nhiệm vụ trong các thử nghiệm này với một hoạt động ngủ ngẫu nhiên.

Định cấu hình tên luồng

Mỗi chủ đề trong Python có một tên.

Chủ đề chính có tên là Main Mainthread. Bạn có thể truy cập luồng chính thông qua gọi đến hàm main_thread () trong mô -đun luồng và sau đó truy cập thành viên tên. Ví dụ:ThreadPoolExecutor-%d_%d” where the first %d indicates the thread pool number and the second %d indicates the thread number, both in the order that thread pools and threads are created.

Chúng ta có thể thấy điều này nếu chúng ta truy cập các luồng trực tiếp bên trong nhóm sau khi phân bổ một số công việc để tất cả các luồng được tạo.

Chúng ta có thể liệt kê tất cả các luồng trong chương trình Python (xử lý) thông qua hàm liệt kê () trong mô -đun luồng, sau đó báo cáo tên cho mỗi.

Ví dụ dưới đây tạo ra một nhóm luồng với số lượng luồng mặc định, phân bổ hoạt động cho nhóm để đảm bảo các luồng được tạo, sau đó báo cáo tên của tất cả các luồng trong chương trình.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

# SuperfastPython.com

# Báo cáo tên mặc định của các luồng trong nhóm luồng

nhập luồngthreading

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# một nhiệm vụ giả không làm gì cả

nhiệm vụ def (tên):task(name):

    passpass

# Tạo một nhóm chủ đề

executor=ThreadPoolExecutor()=ThreadPoolExecutor()

# Thực thi yêu cầu

executor.map(task,range(10)).map(task,range(10))

# Báo cáo tất cả các tên chủ đề

Forthread inthreading.enumerate ():thread inthreading.enumerate():

    print(thread.name)print(thread.name)

# Tắt máy nhóm chủ đề

executor.shutdown().shutdown()

Chạy ví dụ báo cáo tên của tất cả các luồng trong hệ thống, trước tiên hiển thị tên của luồng chính và tên của bốn luồng trong nhóm.

Trong trường hợp này, chỉ có 4 luồng được tạo ra khi các nhiệm vụ được thực hiện rất nhanh. Hãy nhớ lại rằng các chủ đề công nhân được sử dụng sau khi họ hoàn thành việc thực hiện các nhiệm vụ của họ. Khả năng tái sử dụng công nhân này là một lợi ích chính của việc sử dụng nhóm chủ đề.

MAINTHREAD

ThreadPoolExecutor-0_0

ThreadPoolExecutor-0_1

ThreadPoolExecutor-0_2

ThreadPoolExecutor-0_3

ThemePoolExecutor-%D, là tiền tố cho tất cả các luồng trong nhóm luồng và chúng tôi có thể tùy chỉnh nó với một tên có thể có ý nghĩa trong ứng dụng cho các loại nhiệm vụ được thực hiện bởi nhóm.ThreadPoolExecutor-%d” is a prefix for all threads in the thread pool and we can customize it with a name that may be meaningful in the application for the types of tasks executed by the pool.

Tiền tố tên luồng có thể được đặt thông qua đối số của Chủ đề_NAME_PREFIX khi xây dựng nhóm luồng.thread_name_prefix” argument when constructing the thread pool.

Ví dụ dưới đây đặt tiền tố là là Task TaskPool, được chuẩn bị theo tên của mỗi luồng được tạo trong nhóm.TaskPool“, which is prepended to the name of each thread created in the pool.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

# SuperfastPython.com

# Báo cáo tên mặc định của các luồng trong nhóm luồng

nhập luồngthreading

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# một nhiệm vụ giả không làm gì cả

nhiệm vụ def (tên):task(name):

    passpass

# Tạo một nhóm chủ đề

executor=ThreadPoolExecutor(thread_name_prefix='TaskPool')=ThreadPoolExecutor(thread_name_prefix='TaskPool')

# Thực thi yêu cầu

executor.map(task,range(10)).map(task,range(10))

# Báo cáo tất cả các tên chủ đề

Forthread inthreading.enumerate ():thread inthreading.enumerate():

    print(thread.name)print(thread.name)

# Tắt máy nhóm chủ đề

executor.shutdown().shutdown()

Chạy ví dụ báo cáo tên của tất cả các luồng trong hệ thống, trước tiên hiển thị tên của luồng chính và tên của bốn luồng trong nhóm.

MAINTHREAD

TaskPool_0

TaskPool_1

TaskPool_2

TaskPool_3

ThemePoolExecutor-%D, là tiền tố cho tất cả các luồng trong nhóm luồng và chúng tôi có thể tùy chỉnh nó với một tên có thể có ý nghĩa trong ứng dụng cho các loại nhiệm vụ được thực hiện bởi nhóm.

Tiền tố tên luồng có thể được đặt thông qua đối số của Chủ đề_NAME_PREFIX khi xây dựng nhóm luồng.

Ví dụ dưới đây đặt tiền tố là là Task TaskPool, được chuẩn bị theo tên của mỗi luồng được tạo trong nhóm.initializer” argument when creating a thread pool. If the initializer function takes arguments, they can be passed in via the “initargs” argument to the thread pool, which is a tuple of arguments to pass to the initializer function.

# Đặt tiền tố tên luồng tùy chỉnh cho tất cả các luồng trong nhóm

# Tạo một nhóm chủ đề với tiền tố tùy chỉnh tên

Chạy ví dụ báo cáo tên của luồng chính như trước đây, nhưng trong trường hợp này, tên của các luồng trong nhóm luồng với tiền tố tên luồng tùy chỉnh.

Định cấu hình trình khởi tạo

Chủ đề công nhân có thể gọi một chức năng trước khi chúng bắt đầu xử lý các tác vụ.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

# SuperfastPython.com

Đây được gọi là chức năng khởi tạo và có thể được chỉ định thông qua đối số của bộ khởi tạo trên mạng khi tạo một nhóm luồng. Nếu chức năng khởi tạo có các đối số, chúng có thể được truyền qua đối số của INTING INETARGS vào nhóm luồng, đây là một bộ lý đối số để chuyển đến hàm khởi tạo.

Theo mặc định, không có chức năng khởi tạo.time import sleep

Chúng tôi có thể chọn đặt chức năng khởi tạo cho các luồng công nhân nếu chúng tôi muốn mỗi luồng để thiết lập tài nguyên cụ thể cho luồng.random import random

Các ví dụ có thể bao gồm một tệp nhật ký cụ thể theo luồng hoặc kết nối dành riêng cho luồng với tài nguyên từ xa như máy chủ hoặc cơ sở dữ liệu. Tài nguyên sau đó sẽ có sẵn cho tất cả các tác vụ được thực hiện bởi luồng, thay vì được tạo và loại bỏ hoặc mở và đóng cho mỗi nhiệm vụ.threading import current_thread

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# một nhiệm vụ giả không làm gì cả

nhiệm vụ def (tên):initializer_worker():

# Tạo một nhóm chủ đề# get the unique name for this thread

    name=current_thread().namename=current_thread().name

# Thực thi yêu cầu# store the unique worker name in a thread local variable

# Báo cáo tất cả các tên chủ đềprint(f'Initializing worker thread {name}')

# một nhiệm vụ giả ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây

Nhiệm vụ Def (Định danh):task(identifier):

    sleep(random())sleep(random())

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận tên duy nhất# get the unique name

    returnidentifierreturnidentifier

# Tạo một nhóm chủ đề

với ThreadPoolExecutor (MAX_Workers = 2, SititionIzer = initionizer_worker) AsexCutor:ThreadPoolExecutor(max_workers=2,initializer=initializer_worker)as executor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực thi yêu cầu# execute asks

& nbsp; & nbsp; & nbsp; & nbsp;forresult inexecutor.map(task,range(10)):

        print(result)print(result)

Chạy ví dụ, chúng ta có thể thấy rằng hai luồng được khởi tạo trước khi chạy bất kỳ tác vụ nào, sau đó tất cả mười tác vụ được hoàn thành thành công.

Khởi tạo Thread của công nhân ThreadPoolExecutor-0_0

Khởi tạo Thread của công nhân ThreadPoolExecutor-0_1

0

1

2

3

4

5

6

7

8

9

Bây giờ chúng ta đã quen thuộc với cách định cấu hình các nhóm luồng, hãy để tìm hiểu thêm về cách kiểm tra và thao tác các tác vụ thông qua các đối tượng trong tương lai.

Cách sử dụng chi tiết các đối tượng trong tương lai

Các đối tượng trong tương lai được tạo khi chúng ta gọi Substem () để gửi các tác vụ vào ThreadPoolExecutor để được thực thi không đồng bộ. objects are created when we call submit() to send tasks into the ThreadPoolExecutor to be executed asynchronously.

Các đối tượng trong tương lai cung cấp khả năng kiểm tra trạng thái của một tác vụ (ví dụ: nó có đang chạy không?) Và để kiểm soát việc thực hiện nhiệm vụ (ví dụ: hủy). objects provide the capability to check the status of a task (e.g. is it running?) and to control the execution of the task (e.g. cancel).

Trong phần này, chúng tôi sẽ xem xét một số ví dụ về kiểm tra và thao túng các đối tượng trong tương lai được tạo bởi nhóm chủ đề của chúng tôi.Future objects created by our thread pool.

Cụ thể, chúng tôi sẽ xem xét như sau:

  • Cách kiểm tra trạng thái của tương lai
  • Làm thế nào để có được kết quả từ tương lai
  • Cách hủy bỏ tương lai
  • Cách thêm một cuộc gọi lại vào tương lai
  • Làm thế nào để có được ngoại lệ từ tương lai

Đầu tiên, chúng ta hãy xem xét kỹ hơn về vòng đời của một đối tượng trong tương lai.

Vòng đời của một đối tượng tương lai

Một đối tượng trong tương lai được tạo khi chúng ta gọi Substem () cho một tác vụ trên ThreadPoolExecutor.Future object is created when we call submit() for a task on a ThreadPoolExecutor.

Trong khi nhiệm vụ đang thực thi, đối tượng trong tương lai có trạng thái đang chạy.Future object has the status “running“.

Khi nhiệm vụ hoàn thành, nó có trạng thái được thực hiện và nếu hàm đích trả về một giá trị, nó có thể được truy xuất.

Trước khi một nhiệm vụ chạy, nó sẽ được chèn vào hàng đợi các nhiệm vụ cho một chuỗi công nhân để thực hiện và bắt đầu chạy. Ở trạng thái trước khi chạy trước đây, nhiệm vụ này có thể bị hủy bỏ và có trạng thái bị hủy bỏ. Một nhiệm vụ trong trạng thái chạy trên mạng không thể bị hủy bỏ.

Một nhiệm vụ bị hủy bỏ của người Viking cũng luôn ở trạng thái thực hiện.

Trong khi một nhiệm vụ đang chạy, nó có thể tăng một ngoại lệ không bị bắt, khiến việc thực hiện nhiệm vụ dừng lại. Ngoại lệ sẽ được lưu trữ và có thể được truy xuất trực tiếp hoặc sẽ được tăng lại nếu kết quả được cố gắng lấy.

Hình dưới đây tóm tắt vòng đời của một đối tượng trong tương lai.Future object.

Hướng dẫn python reuse threadpoolexecutor - python sử dụng lại threadpoolexecutor
Tổng quan về vòng đời của một đối tượng tương lai Python.

Bây giờ chúng ta đã quen thuộc với vòng đời của một đối tượng trong tương lai, hãy để Lôi nhìn vào cách chúng ta có thể sử dụng kiểm tra và thao túng nó.Future object, let’s look at how we might use check and manipulate it.

Cách kiểm tra trạng thái của tương lai

Làm thế nào để có được kết quả từ tương laiFuture object that we might want to check: running and done.

Cách hủy bỏ tương laiTrue if the Future object is in that state or False otherwise; for example:

  • Cách thêm một cuộc gọi lại vào tương lai: Returns True if the task is currently running.
  • Làm thế nào để có được ngoại lệ từ tương lai: Returns True if the task has completed or was cancelled.

Đầu tiên, chúng ta hãy xem xét kỹ hơn về vòng đời của một đối tượng trong tương lai.Future object.

Vòng đời của một đối tượng tương lai

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

Một đối tượng trong tương lai được tạo khi chúng ta gọi Substem () cho một tác vụ trên ThreadPoolExecutor.

Trong khi nhiệm vụ đang thực thi, đối tượng trong tương lai có trạng thái đang chạy.

Khi nhiệm vụ hoàn thành, nó có trạng thái được thực hiện và nếu hàm đích trả về một giá trị, nó có thể được truy xuất.time import sleep

Trước khi một nhiệm vụ chạy, nó sẽ được chèn vào hàng đợi các nhiệm vụ cho một chuỗi công nhân để thực hiện và bắt đầu chạy. Ở trạng thái trước khi chạy trước đây, nhiệm vụ này có thể bị hủy bỏ và có trạng thái bị hủy bỏ. Một nhiệm vụ trong trạng thái chạy trên mạng không thể bị hủy bỏ.concurrent.futures import ThreadPoolExecutor

Một nhiệm vụ bị hủy bỏ của người Viking cũng luôn ở trạng thái thực hiện.concurrent.futures import wait

Trong khi một nhiệm vụ đang chạy, nó có thể tăng một ngoại lệ không bị bắt, khiến việc thực hiện nhiệm vụ dừng lại. Ngoại lệ sẽ được lưu trữ và có thể được truy xuất trực tiếp hoặc sẽ được tăng lại nếu kết quả được cố gắng lấy.

Hình dưới đây tóm tắt vòng đời của một đối tượng trong tương lai.work():

    sleep(0.5)sleep(0.5)

# Tạo một nhóm chủ đề

Tổng quan về vòng đời của một đối tượng tương lai Python.ThreadPoolExecutor()asexecutor:

Bây giờ chúng ta đã quen thuộc với vòng đời của một đối tượng trong tương lai, hãy để Lôi nhìn vào cách chúng ta có thể sử dụng kiểm tra và thao túng nó.# start one thread

    future=executor.submit(work)future=executor.submit(work)

Có hai loại trạng thái bình thường của một đối tượng trong tương lai mà chúng ta có thể muốn kiểm tra: chạy và thực hiện.# confirm that the task is running

    running=future.running()running=future.running()

    done=future.done()done =future.done()

Mỗi người có chức năng riêng trả về đúng nếu đối tượng tương lai ở trạng thái đó hoặc sai; Ví dụ:print(f'Future running={running}, done={done}')

Running (): Trả về đúng nếu nhiệm vụ hiện đang chạy.# wait for the task to complete

    wait([future])wait([future])

DOME (): Trả về đúng nếu nhiệm vụ đã hoàn thành hoặc bị hủy.# confirm that the task is done

    running=future.running()running=future.running()

    done=future.done()done=future.done()

Mỗi người có chức năng riêng trả về đúng nếu đối tượng tương lai ở trạng thái đó hoặc sai; Ví dụ:print(f'Future running={running}, done={done}')

Running (): Trả về đúng nếu nhiệm vụ hiện đang chạy.

DOME (): Trả về đúng nếu nhiệm vụ đã hoàn thành hoặc bị hủy.

Chúng ta có thể phát triển các ví dụ đơn giản để chứng minh cách kiểm tra trạng thái của một đối tượng trong tương lai.

Làm thế nào để có được kết quả từ tương lai

Khi một nhiệm vụ được hoàn thành, chúng ta có thể lấy kết quả từ nhiệm vụ bằng cách gọi hàm kết quả () trong tương lai.result() function on the Future.

Điều này trả về kết quả từ chức năng trả về của tác vụ chúng tôi đã thực hiện hoặc không có nếu hàm không trả về giá trị.None if the function did not return a value.

Hàm sẽ chặn cho đến khi hoàn thành nhiệm vụ và kết quả có thể được truy xuất. Nếu nhiệm vụ đã được hoàn thành, nó sẽ trả về kết quả ngay lập tức.

Ví dụ dưới đây cho thấy cách lấy kết quả từ một đối tượng trong tương lai.Future object.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

# SuperfastPython.com

# Nhận kết quả từ một nhiệm vụ trong tương lai đã hoàn thành

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work():

    sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp; trả lại "tất cả đã hoàn thành"return"all done"

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# bắt đầu một chủ đề# start one thread

    future=executor.submit(work)future= executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả từ nhiệm vụ, chờ nhiệm vụ hoàn thành# get the result from the task, wait for task to complete

    result=future.result()result=future.result()

& nbsp; & nbsp; & nbsp; & nbsp; in (kết quả f'got: {result} ')print(f'Got Result: {result}')

Chạy ví dụ gửi nhiệm vụ sau đó cố gắng truy xuất kết quả, chặn cho đến khi kết quả có sẵn, sau đó báo cáo kết quả đã nhận được.

Chúng tôi cũng có thể đặt thời gian chờ trong thời gian chúng tôi muốn chờ kết quả trong vài giây.

Nếu thời gian chờ trôi qua trước khi chúng ta nhận được kết quả, một thời gian được nâng lên.TimeoutError is raised.

Ví dụ dưới đây cho thấy thời gian chờ, cho thấy cách từ bỏ việc chờ đợi trước khi hoàn thành nhiệm vụ.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# SuperfastPython.com

# Nhận kết quả từ một nhiệm vụ trong tương lai đã hoàn thành

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ giả sẽ ngủ trong giây látconcurrent.futures import TimeoutError

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work():

    sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp; trả lại "tất cả đã hoàn thành"return"all done"

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()as executor:

& nbsp; & nbsp; & nbsp; & nbsp;# bắt đầu một chủ đề# start one thread

    future=executor.submit(work)future=executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả từ nhiệm vụ, chờ nhiệm vụ hoàn thành# get the result from the task, wait for task to complete

    try:try:

        result=future.result(timeout=0.5)result=future.result(timeout=0.5)

& nbsp; & nbsp; & nbsp; & nbsp; in (kết quả f'got: {result} ')print(f'Got Result: {result}')

Chạy ví dụ gửi nhiệm vụ sau đó cố gắng truy xuất kết quả, chặn cho đến khi kết quả có sẵn, sau đó báo cáo kết quả đã nhận được.except TimeoutError:

Chúng tôi cũng có thể đặt thời gian chờ trong thời gian chúng tôi muốn chờ kết quả trong vài giây.print('Gave up waiting for a result')

Nếu thời gian chờ trôi qua trước khi chúng ta nhận được kết quả, một thời gian được nâng lên.

Ví dụ dưới đây cho thấy thời gian chờ, cho thấy cách từ bỏ việc chờ đợi trước khi hoàn thành nhiệm vụ.

# Đặt thời gian chờ khi nhận được kết quả từ một tương lai

từ đồng thời.

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp;submit() or map() that the tasks are added to an internal queue of work from which worker threads can remove the tasks and execute them.

& nbsp; & nbsp; & nbsp; & nbsp; ngoại trừ timeouterror:cancel() on the Future object associated with the task. The cancel() function will return True if the task was cancelled, False otherwise.

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;

Chạy ví dụ cho thấy rằng chúng tôi đã từ bỏ việc chờ kết quả sau nửa giây.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

# SuperfastPython.com

Đã từ bỏ việc chờ kết quả

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ giả sẽ ngủ trong giây látconcurrent.futures import wait

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work(sleep_time):

    sleep(sleep_time)sleep(sleep_time)

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor(1)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# bắt đầu một chủ đề# start a long running task

    future1=executor.submit(work,2)future1=executor.submit(work,2)

    running=future1.running()running=future1.running()

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả từ nhiệm vụ, chờ nhiệm vụ hoàn thànhprint(f'First task running={running}')

& nbsp; & nbsp; & nbsp; & nbsp; in (kết quả f'got: {result} ')# start a second

    future2=executor.submit(work,0.1)future2=executor.submit(work,0.1)

    running=future2.running()running= future2.running()

Chạy ví dụ gửi nhiệm vụ sau đó cố gắng truy xuất kết quả, chặn cho đến khi kết quả có sẵn, sau đó báo cáo kết quả đã nhận được.print(f'Second task running={running}')

Chúng tôi cũng có thể đặt thời gian chờ trong thời gian chúng tôi muốn chờ kết quả trong vài giây.# cancel the second task

    was_cancelled=future2.cancel()was_cancelled= future2.cancel()

Nếu thời gian chờ trôi qua trước khi chúng ta nhận được kết quả, một thời gian được nâng lên.print(f'Second task was cancelled: {was_cancelled}')

Ví dụ dưới đây cho thấy thời gian chờ, cho thấy cách từ bỏ việc chờ đợi trước khi hoàn thành nhiệm vụ.# wait for the second task to finish, just in case

    wait([future2])wait([future2])

# Đặt thời gian chờ khi nhận được kết quả từ một tương lai# confirm it was cancelled

    running=future2.running()running=future2.running()

    cancelled=future2.cancelled()cancelled=future2.cancelled()

    done=future2.done()done =future2.done()

& nbsp; & nbsp; & nbsp; & nbsp; print (f'Second Task Running = {Running}, đã hủy = {đã hủy}, DOWN = {DOWN} ')print(f'Second task running={running}, cancelled={cancelled}, done={done}')

& nbsp; & nbsp; & nbsp; & nbsp;# chờ cho nhiệm vụ chạy dài để hoàn thành# wait for the long running task to finish

    wait([future1])wait([future1])

Chạy ví dụ, chúng ta có thể thấy rằng nhiệm vụ đầu tiên được bắt đầu và đang chạy bình thường.

Nhiệm vụ thứ hai được lên lịch và chưa chạy vì nhóm luồng bị chiếm giữ với nhiệm vụ đầu tiên. Sau đó chúng tôi hủy bỏ nhiệm vụ thứ hai và xác nhận rằng nó thực sự không chạy; Nó đã bị hủy và được thực hiện.

Nhiệm vụ đầu tiên chạy = Đúng

Nhiệm vụ thứ hai đang chạy = Sai

Nhiệm vụ thứ hai đã bị hủy: Đúng

Nhiệm vụ thứ hai đang chạy = false, đã hủy = true, thực hiện = true

Hủy một tương lai đang chạy

Bây giờ, hãy để cố gắng hủy bỏ một nhiệm vụ đã hoàn thành chạy.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

# SuperfastPython.com

# Ví dụ về việc cố gắng hủy một nhiệm vụ đang chạy thông qua tương lai của nó

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

từ đồng thời.concurrent.futures import wait

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work (sleep_time):work(sleep_time):

    sleep(sleep_time)sleep(sleep_time)

# Tạo một nhóm chủ đề

với ThreadPoolExecutor (1) Asexecutor:ThreadPoolExecutor(1)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# bắt đầu một nhiệm vụ chạy dài# start a long running task

    future=executor.submit(work,2)future=executor.submit(work,2)

    running=future.running()running=future.running()

& nbsp; & nbsp; & nbsp; & nbsp; in (f'task chạy = {chạy} '))print(f'Task running={running}')

& nbsp; & nbsp; & nbsp; & nbsp;# cố gắng hủy bỏ nhiệm vụ# try to cancel the task

    was_cancelled=future.cancel()was_cancelled=future.cancel()

& nbsp; & nbsp; & nbsp; & nbsp; in (f'task đã bị hủy: {was_cancelled} ')print(f'Task was cancelled: {was_cancelled}')

& nbsp; & nbsp; & nbsp; & nbsp;# chờ cho nhiệm vụ hoàn thành# wait for the task to finish

    wait([future])wait([future])

& nbsp; & nbsp; & nbsp; & nbsp;# kiểm tra xem nó có bị hủy không# check if it was cancelled

    running=future.running()running=future.running()

    cancelled=future.cancelled()cancelled =future.cancelled()

    done=future.done()done=future.done()

& nbsp; & nbsp; & nbsp; & nbsp;print(f'Task running={running}, cancelled={cancelled}, done={done}')

Chạy ví dụ, chúng ta có thể thấy rằng nhiệm vụ đã được bắt đầu theo bình thường.

Sau đó, chúng tôi đã cố gắng hủy bỏ nhiệm vụ, nhưng điều này không thành công, như chúng tôi mong đợi, vì nhiệm vụ đã được thực hiện.

Sau đó chúng tôi chờ đợi nhiệm vụ hoàn thành và sau đó kiểm tra trạng thái của nó. Chúng ta có thể thấy rằng nhiệm vụ không còn chạy và không bị hủy, như chúng ta mong đợi, nhưng được đánh dấu là không được thực hiện.

Điều này là đáng ngạc nhiên vì nhiệm vụ đã được hoàn thành thành công. Điều này có khả năng chỉ ra tình huống nhận được yêu cầu hủy bỏ nhưng không được thực hiện.

Nhiệm vụ chạy = Đúng

Nhiệm vụ đã bị hủy: Sai

Tác vụ chạy = false, hủy = false, xong = true

Hủy một tương lai đã hoàn thành

Xem xét những gì sẽ xảy ra nếu chúng tôi cố gắng hủy bỏ một nhiệm vụ đã được thực hiện.

Chúng tôi có thể hy vọng rằng việc hủy bỏ một nhiệm vụ đã được thực hiện không có hiệu lực và điều này xảy ra.

Điều này có thể được chứng minh với một ví dụ ngắn.

Chúng tôi bắt đầu và chạy một nhiệm vụ theo bình thường, sau đó chờ nó hoàn thành và báo cáo trạng thái của nó. Sau đó chúng tôi cố gắng hủy bỏ nhiệm vụ.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

# SuperfastPython.com

# Ví dụ về việc cố gắng hủy một nhiệm vụ đang chạy thông qua tương lai của nó

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

từ đồng thời.concurrent.futures import wait

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work (sleep_time):work(sleep_time):

    sleep(sleep_time)sleep(sleep_time)

# Tạo một nhóm chủ đề

với ThreadPoolExecutor (1) Asexecutor:ThreadPoolExecutor(1)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# bắt đầu một nhiệm vụ chạy dài# start a long running task

    future=executor.submit(work,2)future=executor.submit(work,2)

    running=future.running()running=future.running()

& nbsp; & nbsp; & nbsp; & nbsp;# chờ cho nhiệm vụ hoàn thành# wait for the task to finish

    wait([future])wait([future])

& nbsp; & nbsp; & nbsp; & nbsp;# kiểm tra xem nó có bị hủy không# check the status

    running=future.running()running=future.running()

    cancelled=future.cancelled()cancelled =future.cancelled()

    done=future.done()done=future.done()

& nbsp; & nbsp; & nbsp; & nbsp;print(f'Task running={running}, cancelled={cancelled}, done={done}')

& nbsp; & nbsp; & nbsp; & nbsp;# cố gắng hủy bỏ nhiệm vụ# try to cancel the task

    was_cancelled=future.cancel()was_cancelled=future.cancel()

& nbsp; & nbsp; & nbsp; & nbsp; in (f'task đã bị hủy: {was_cancelled} ')print(f'Task was cancelled: {was_cancelled}')

& nbsp; & nbsp; & nbsp; & nbsp;# kiểm tra xem nó có bị hủy không# check if it was cancelled

    running=future.running()running =future.running()

    cancelled=future.cancelled()cancelled=future.cancelled()

    done=future.done()done=future.done()

& nbsp; & nbsp; & nbsp; & nbsp;print(f'Task running={running}, cancelled={cancelled}, done={done}')

Chạy ví dụ, chúng ta có thể thấy rằng nhiệm vụ đã được bắt đầu theo bình thường.

Sau đó, chúng tôi đã cố gắng hủy bỏ nhiệm vụ, nhưng điều này không thành công, như chúng tôi mong đợi, vì nhiệm vụ đã được thực hiện.

Tác vụ chạy = false, hủy = false, xong = true

Nhiệm vụ đã bị hủy: Sai

Tác vụ chạy = false, hủy = false, xong = true

Cách thêm một cuộc gọi lại vào tương lai

Chúng tôi đã thấy ở trên làm thế nào để thêm một cuộc gọi lại vào một tương lai. Tuy nhiên, hãy để Lôi xem xét một số ví dụ khác cho tính đầy đủ bao gồm một số trường hợp cạnh.Future. Nevertheless, let’s look at some more examples for completeness including some edge cases.

Chúng ta có thể đăng ký một hoặc nhiều hàm gọi lại trên một đối tượng trong tương lai bằng cách gọi hàm add_done_callback () và chỉ định tên của hàm để gọi.Future object by calling the add_done_callback() function and specifying the name of the function to call.

Các chức năng gọi lại sẽ được gọi với đối tượng trong tương lai như một đối số ngay sau khi hoàn thành nhiệm vụ. Nếu có nhiều chức năng gọi lại được đăng ký, thì chúng sẽ được gọi theo thứ tự chúng được đăng ký và mọi trường hợp ngoại lệ trong mỗi hàm gọi lại sẽ bị bắt, ghi nhật ký và bỏ qua.Future object as an argument immediately after the completion of the task. If more than one callback function is registered, then they will be called in the order they were registered and any exceptions within each callback function will be caught, logged and ignored.

Cuộc gọi lại sẽ được gọi bởi luồng công nhân đã thực hiện nhiệm vụ.

Ví dụ dưới đây trình bày cách thêm chức năng gọi lại vào một đối tượng trong tương lai.Future object.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# SuperfastPython.com

# Thêm tùy chọn gọi lại cho một đối tượng trong tương lai

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

từ đồng thời.concurrent.futures import wait

# chức năng gọi lại để gọi khi hoàn thành tác vụ

Def Custom_callback (Tương lai):custom_callback(future):

& nbsp; & nbsp; & nbsp; & nbsp; in ('gọi lại tùy chỉnh được gọi là')print('Custom callback was called')

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work():

    sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp; in ('nhiệm vụ được thực hiện')print('Task is done')

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực hiện nhiệm vụ# execute the task

    future=executor.submit(work)future= executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp;# Thêm gọi lại tùy chỉnh# add the custom callback

    future.add_done_callback(custom_callback)future.add_done_callback(custom_callback)

& nbsp; & nbsp; & nbsp; & nbsp;# chờ hoàn thành nhiệm vụ# wait for the task to complete

    wait([future])wait([future])

Chạy ví dụ, chúng ta có thể thấy rằng nhiệm vụ được hoàn thành trước, sau đó cuộc gọi lại được thực thi như chúng ta mong đợi.

Nhiệm vụ được thực hiện

Gọi lại tùy chỉnh được gọi là

Lỗi phổ biến khi sử dụng các cuộc gọi lại trong tương lai

Một lỗi phổ biến là quên thêm đối tượng trong tương lai làm đối số vào cuộc gọi lại tùy chỉnh.Future object as an argument to the custom callback.

Ví dụ:

# chức năng gọi lại để gọi khi hoàn thành tác vụ

Def Custom_callback (Tương lai):custom_callback():

& nbsp; & nbsp; & nbsp; & nbsp; in ('gọi lại tùy chỉnh được gọi là')print('Custom callback was called')

# Nhiệm vụ giả sẽ ngủ trong giây látTypeError as follows:

Nhiệm vụ được thực hiện

Gọi lại tùy chỉnh được gọi là

Lỗi phổ biến khi sử dụng các cuộc gọi lại trong tương lai

Một lỗi phổ biến là quên thêm đối tượng trong tương lai làm đối số vào cuộc gọi lại tùy chỉnh.

Ví dụ:TypeError makes it clear how to fix the issue: add a single argument to the function for the future object, even if you don’t intend on using it in your callback.

def Custom_callback ():

Nếu bạn đăng ký chức năng này và cố gắng chạy mã, bạn sẽ nhận được một kiểu mẫu như sau:Future objects for tasks that are cancelled.

Gọi gọi điện ngoại lệ cho

...

TypeError: Custom_callback () lấy 0 đối số vị trí nhưng 1 đã được đưa ra

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

# SuperfastPython.com

# Thêm tùy chọn gọi lại cho một đối tượng trong tương lai

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

từ đồng thời.concurrent.futures import wait

# chức năng gọi lại để gọi khi hoàn thành tác vụ

Def Custom_callback (Tương lai):custom_callback(future):

& nbsp; & nbsp; & nbsp; & nbsp; in ('gọi lại tùy chỉnh được gọi là')print('Custom callback was called')

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work(sleep_time):

    sleep(sleep_time)sleep(sleep_time)

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor(1)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực hiện nhiệm vụ# start a long running task

    future1=executor.submit(work,2)future1= executor.submit(work,2)

    running=future1.running()running=future1.running()

& nbsp; & nbsp; & nbsp; & nbsp;# Thêm gọi lại tùy chỉnhprint(f'First task running={running}')

& nbsp; & nbsp; & nbsp; & nbsp;# chờ hoàn thành nhiệm vụ# start a second

    future2=executor.submit(work,0.1)future2=executor.submit(work,0.1)

    running=future2.running()running=future2.running()

Chạy ví dụ, chúng ta có thể thấy rằng nhiệm vụ được hoàn thành trước, sau đó cuộc gọi lại được thực thi như chúng ta mong đợi.print(f'Second task running={running}')

& nbsp; & nbsp; & nbsp; & nbsp;# Thêm gọi lại tùy chỉnh# add the custom callback

    future2.add_done_callback(custom_callback)future2.add_done_callback(custom_callback)

& nbsp; & nbsp; & nbsp; & nbsp;# chờ hoàn thành nhiệm vụ# cancel the second task

    was_cancelled=future2.cancel()was_cancelled= future2.cancel()

Chạy ví dụ, chúng ta có thể thấy rằng nhiệm vụ được hoàn thành trước, sau đó cuộc gọi lại được thực thi như chúng ta mong đợi.print(f'Second task was cancelled: {was_cancelled}')

Nhiệm vụ được thực hiện# explicitly wait for all tasks to complete

    wait([future1,future2])wait([future1, future2])

Gọi lại tùy chỉnh được gọi là

Nhiệm vụ thứ hai được lên lịch nhưng không có cơ hội chạy trước khi chúng tôi hủy nó. Cuộc gọi lại được chạy ngay sau khi chúng tôi hủy nhiệm vụ, sau đó chúng tôi báo cáo trong chuỗi chính thực sự nhiệm vụ đã bị hủy chính xác.

Nhiệm vụ đầu tiên chạy = Đúng

Nhiệm vụ thứ hai đang chạy = Sai

Gọi lại tùy chỉnh được gọi là

Nhiệm vụ thứ hai đã bị hủy: Đúng

Làm thế nào để có được ngoại lệ từ tương lai

Một nhiệm vụ có thể tăng một ngoại lệ trong quá trình thực hiện.

Nếu chúng ta có thể dự đoán ngoại lệ, chúng ta có thể bọc các phần của chức năng tác vụ của mình trong một khối Except thử và xử lý ngoại lệ trong nhiệm vụ.

Nếu một ngoại lệ bất ngờ xảy ra trong nhiệm vụ của chúng tôi, nhiệm vụ sẽ ngừng thực thi.

Chúng tôi không thể biết dựa trên trạng thái nhiệm vụ liệu một ngoại lệ đã được nêu ra, nhưng chúng tôi có thể kiểm tra trực tiếp một ngoại lệ.

Sau đó, chúng ta có thể truy cập ngoại lệ thông qua hàm Exception (). Thay vào đó, ngoại lệ sẽ được tăng lại khi gọi hàm kết quả () khi cố gắng có kết quả.exception() function. Alternately, the exception will be re-raised when calling the result() function when trying to get a result.

Chúng ta có thể chứng minh điều này với một ví dụ.

Ví dụ dưới đây sẽ tăng giá trịerRor trong nhiệm vụ sẽ không bị bắt nhưng thay vào đó sẽ bị nhóm luồng bắt để chúng tôi truy cập sau này.ValueError within the task that will not be caught but instead will be caught by the thread pool for us to access later.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

# SuperfastPython.com

# Ví dụ về việc xử lý một ngoại lệ được nêu trong một nhiệm vụ

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

từ đồng thời.concurrent.futures import wait

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work():

    sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp; tăng ngoại lệ ('Đây là giả!')raise Exception('This is Fake!')

& nbsp; & nbsp; & nbsp; & nbsp; return "không bao giờ đến đây"return"never gets here"

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực hiện nhiệm vụ của chúng tôi# execute our task

    future=executor.submit(work)future=executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp;# chờ hoàn thành nhiệm vụ# wait for the task to complete

    wait([future])wait([future])

& nbsp; & nbsp; & nbsp; & nbsp;# Kiểm tra trạng thái của nhiệm vụ sau khi hoàn thành# check the status of the task after it has completed

    running=future.running()running=future.running()

    cancelled=future.cancelled()cancelled =future.cancelled()

    done=future.done()done=future.done()

& nbsp; & nbsp; & nbsp; & nbsp;print(f'Task running={running}, cancelled={cancelled}, done={done}')

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận ngoại lệ# get the exception

    exception=future.exception()exception=future.exception()

    print(f'Exception={exception}')print(f'Exception={exception}')

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả từ nhiệm vụ# get the result from the task

    try:try:

        result=future.result()result=future.result()

& nbsp; & nbsp; & nbsp; & nbsp; ngoại trừ ngoại lệ:except Exception:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print('Unable to get the result')

Chạy ví dụ bắt đầu nhiệm vụ bình thường, ngủ trong một giây.

Nhiệm vụ sau đó đặt ra một ngoại lệ được bắt bởi nhóm chủ đề. Nhóm chủ đề lưu trữ ngoại lệ và nhiệm vụ được hoàn thành.

Chúng ta có thể thấy rằng sau khi hoàn thành nhiệm vụ, nó được đánh dấu là không chạy, không bị hủy và thực hiện.

Sau đó, chúng tôi truy cập ngoại lệ từ nhiệm vụ, phù hợp với ngoại lệ mà chúng tôi cố tình nêu ra.

Cố gắng truy cập kết quả thông qua hàm kết quả () không thành công và chúng tôi bắt được ngoại lệ tương tự được nêu trong nhiệm vụ.

Tác vụ chạy = false, hủy = false, xong = true

Ngoại lệ = đây là giả!

Không thể có được kết quả

Các cuộc gọi lại vẫn được gọi nếu một tác vụ làm tăng một ngoại lệ

Chúng tôi có thể tự hỏi nếu chúng tôi đăng ký một chức năng gọi lại với một tương lai liệu nó vẫn sẽ thực thi nếu nhiệm vụ tăng một ngoại lệ.Future whether it will still execute if the task raises an exception.

Như chúng ta có thể mong đợi, cuộc gọi lại được thực thi ngay cả khi nhiệm vụ tăng một ngoại lệ.

Chúng tôi có thể kiểm tra điều này bằng cách cập nhật ví dụ trước đó để đăng ký chức năng gọi lại trước khi tác vụ thất bại với một ngoại lệ.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

# SuperfastPython.com

# Ví dụ về việc xử lý một ngoại lệ được nêu trong một nhiệm vụ

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

từ đồng thời.concurrent.futures import wait

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():custom_callback(future):

& nbsp; & nbsp; & nbsp; & nbsp; tăng ngoại lệ ('Đây là giả!')print('Custom callback was called')

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work():

    sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp; tăng ngoại lệ ('Đây là giả!')raise Exception('This is Fake!')

& nbsp; & nbsp; & nbsp; & nbsp; return "không bao giờ đến đây"return"never gets here"

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực hiện nhiệm vụ của chúng tôi# execute our task

    future=executor.submit(work)future=executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp;# chờ hoàn thành nhiệm vụ# add the custom callback

    future.add_done_callback(custom_callback)future.add_done_callback(custom_callback)

& nbsp; & nbsp; & nbsp; & nbsp;# chờ hoàn thành nhiệm vụ# wait for the task to complete

    wait([future])wait([future])

& nbsp; & nbsp; & nbsp; & nbsp;# Kiểm tra trạng thái của nhiệm vụ sau khi hoàn thành# check the status of the task after it has completed

    running=future.running()running=future.running()

    cancelled=future.cancelled()cancelled =future.cancelled()

    done=future.done()done=future.done()

& nbsp; & nbsp; & nbsp; & nbsp;print(f'Task running={running}, cancelled={cancelled}, done={done}')

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận ngoại lệ# get the exception

    exception=future.exception()exception=future.exception()

    print(f'Exception={exception}')print(f'Exception={exception}')

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả từ nhiệm vụ# get the result from the task

    try:try:

        result=future.result()result=future.result()

& nbsp; & nbsp; & nbsp; & nbsp; ngoại trừ ngoại lệ:except Exception:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print('Unable to get the result')

Chạy ví dụ bắt đầu nhiệm vụ như trước, nhưng lần này đăng ký chức năng gọi lại.

Khi tác vụ thất bại với một ngoại lệ, cuộc gọi lại được gọi ngay lập tức. Chủ đề chính sau đó báo cáo trạng thái của tác vụ thất bại và các chi tiết của ngoại lệ.

Gọi lại tùy chỉnh được gọi là

Tác vụ chạy = false, hủy = false, xong = true

Ngoại lệ = đây là giả!

Không thể có được kết quả

Khi nào nên sử dụng ThreadPoolExecutor

ThreadPoolExecutor mạnh mẽ và linh hoạt, mặc dù nó không phù hợp với tất cả các tình huống mà bạn cần chạy một nhiệm vụ nền.ThreadPoolExecutor is powerful and flexible, although it is not suited for all situations where you need to run a background task.

Trong phần này, chúng tôi sẽ xem xét một số trường hợp chung mà nó phù hợp, và nơi đó là, sau đó chúng tôi sẽ xem xét các lớp nhiệm vụ rộng và lý do tại sao chúng hoặc không phù hợp với ThreadPoolExecutor.ThreadPoolExecutor.

Sử dụng ThreadPoolExecutor khi

  • Nhiệm vụ của bạn có thể được xác định bởi một chức năng thuần túy không có tác dụng phụ hoặc phụ.
  • Nhiệm vụ của bạn có thể phù hợp với một chức năng Python duy nhất, có khả năng làm cho nó đơn giản và dễ hiểu.
  • Bạn cần thực hiện cùng một nhiệm vụ nhiều lần, ví dụ: Nhiệm vụ đồng nhất.
  • Bạn cần áp dụng cùng một hàm cho mỗi đối tượng trong một bộ sưu tập trong vòng lặp.

Các nhóm luồng hoạt động tốt nhất khi áp dụng cùng một hàm thuần túy trên một tập hợp các dữ liệu khác nhau (ví dụ: các tác vụ đồng nhất, dữ liệu không đồng nhất). Điều này làm cho mã dễ dàng hơn để đọc và gỡ lỗi. Đây không phải là một quy tắc, chỉ là một gợi ý nhẹ nhàng.

Sử dụng nhiều ThreadPoolExecutor khi

  • Bạn cần thực hiện các nhóm các loại nhiệm vụ khác nhau; Một nhóm chủ đề có thể được sử dụng cho từng loại nhiệm vụ.
  • Bạn cần thực hiện một đường ống của các nhiệm vụ hoặc hoạt động; Một nhóm chủ đề có thể được sử dụng cho mỗi bước.

Các nhóm luồng có thể hoạt động trên các tác vụ của các loại khác nhau (ví dụ: các tác vụ không đồng nhất), mặc dù nó có thể giúp tổ chức chương trình của bạn và gỡ lỗi dễ dàng nếu một nhóm luồng riêng biệt chịu trách nhiệm cho từng loại nhiệm vụ. Đây không phải là một quy tắc, chỉ là một gợi ý nhẹ nhàng.

Don Tiết sử dụng ThreadPoolExecutor khi

  • Bạn có một nhiệm vụ duy nhất; Cân nhắc sử dụng lớp luồng với đối số đích.Thread class with the target argument.
  • Bạn có các nhiệm vụ chạy dài, chẳng hạn như giám sát hoặc lập lịch; Xem xét mở rộng lớp chủ đề.Thread class.
  • Các chức năng nhiệm vụ của bạn yêu cầu trạng thái; Xem xét mở rộng lớp chủ đề.Thread class.
  • Nhiệm vụ của bạn đòi hỏi sự phối hợp; Xem xét sử dụng một chủ đề và các mẫu như hàng rào hoặc semaphore.Thread and patterns like a Barrier or Semaphore.
  • Nhiệm vụ của bạn yêu cầu đồng bộ hóa; Cân nhắc sử dụng một chủ đề và khóa.Thread and Locks.
  • Bạn yêu cầu một chủ đề kích hoạt trên một sự kiện; Xem xét sử dụng lớp chủ đề.Thread class.

Điểm ngọt ngào cho các nhóm chủ đề là trong việc gửi nhiều nhiệm vụ tương tự, kết quả có thể được sử dụng sau này trong chương trình. Các nhiệm vụ không phù hợp với bản tóm tắt này có lẽ không phù hợp với các nhóm chủ đề. Đây không phải là một quy tắc, chỉ là một gợi ý nhẹ nhàng.

Bạn có biết bất kỳ trường hợp tốt hay xấu nào khác khi sử dụng ThreadPoolExecutor không? Hãy cho tôi biết trong các bình luận bên dưới.
Let me know in the comments below.

Sử dụng các chủ đề cho các tác vụ gắn IO

Bạn nên sử dụng các chủ đề cho các nhiệm vụ ràng buộc IO.

Nhiệm vụ ràng buộc IO là một loại nhiệm vụ liên quan đến việc đọc từ hoặc ghi vào một thiết bị, tệp hoặc kết nối ổ cắm.

Các hoạt động liên quan đến đầu vào và đầu ra (IO) và tốc độ của các hoạt động này bị ràng buộc bởi thiết bị, ổ cứng hoặc kết nối mạng. Đây là lý do tại sao các nhiệm vụ này được gọi là giới hạn IO.

CPU thực sự nhanh. CPU hiện đại, giống như 4GHz, có thể thực hiện 4 tỷ hướng dẫn mỗi giây và bạn có thể có nhiều hơn một CPU trong hệ thống của mình.

Làm IO rất chậm so với tốc độ của CPU.

Tương tác với các thiết bị, đọc và ghi các tệp và kết nối ổ cắm liên quan đến việc gọi hướng dẫn trong hệ điều hành của bạn (kernel), sẽ chờ hoàn thành thao tác. Nếu hoạt động này là trọng tâm chính cho CPU của bạn, chẳng hạn như thực hiện trong chuỗi chính của chương trình Python của bạn, thì CPU của bạn sẽ chờ nhiều mili giây hoặc thậm chí nhiều giây không làm gì cả.

Đó là khả năng hàng tỷ hoạt động ngăn chặn thực thi.

Chúng tôi có thể giải phóng CPU khỏi các hoạt động ràng buộc IO bằng cách thực hiện các hoạt động ràng buộc IO trên một chủ đề thực thi khác. Điều này cho phép CPU bắt đầu quá trình và chuyển nó cho hệ điều hành (kernel) để chờ đợi và giải phóng nó để thực hiện trong một luồng ứng dụng khác.

Có nhiều hơn nữa cho nó dưới vỏ bọc, nhưng đây là ý chính.

Do đó, các tác vụ chúng tôi thực thi với một ThreadPoolExecutor phải là các nhiệm vụ liên quan đến các hoạt động IO.ThreadPoolExecutor should be tasks that involve IO operations.

Những ví dụ bao gồm:

  • Đọc hoặc viết một tập tin từ ổ cứng.
  • Đọc hoặc ghi vào đầu ra tiêu chuẩn, đầu vào hoặc lỗi (stdin, stdout, stderr).
  • In một tài liệu.
  • Tải xuống hoặc tải lên một tập tin.
  • Truy vấn một máy chủ.
  • Truy vấn một cơ sở dữ liệu.
  • Chụp ảnh hoặc ghi video.
  • Và nhiều hơn nữa.

Nếu nhiệm vụ của bạn không bị ràng buộc, có lẽ các chủ đề và sử dụng nhóm luồng là không phù hợp.

Don Tiết sử dụng các chủ đề cho các nhiệm vụ ràng buộc CPU

Bạn có thể không nên sử dụng các chủ đề cho các tác vụ gắn CPU.

Nhiệm vụ ràng buộc CPU là một loại nhiệm vụ liên quan đến việc thực hiện tính toán và không liên quan đến IO.

Các hoạt động chỉ liên quan đến dữ liệu trong bộ nhớ chính (RAM) hoặc bộ đệm (bộ đệm CPU) và thực hiện các tính toán trên hoặc với dữ liệu đó. Như vậy, giới hạn về các hoạt động này là tốc độ của CPU. Đây là lý do tại sao chúng tôi gọi chúng là các nhiệm vụ ràng buộc CPU.

Những ví dụ bao gồm:

  • Đọc hoặc viết một tập tin từ ổ cứng.
  • Đọc hoặc ghi vào đầu ra tiêu chuẩn, đầu vào hoặc lỗi (stdin, stdout, stderr).
  • In một tài liệu.
  • Tải xuống hoặc tải lên một tập tin.
  • Truy vấn một máy chủ.
  • Truy vấn một cơ sở dữ liệu.

Chụp ảnh hoặc ghi video.

Và nhiều hơn nữa.

Nếu nhiệm vụ của bạn không bị ràng buộc, có lẽ các chủ đề và sử dụng nhóm luồng là không phù hợp.

Don Tiết sử dụng các chủ đề cho các nhiệm vụ ràng buộc CPU

Bạn có thể không nên sử dụng các chủ đề cho các tác vụ gắn CPU.

Nhiệm vụ ràng buộc CPU là một loại nhiệm vụ liên quan đến việc thực hiện tính toán và không liên quan đến IO.

Các hoạt động chỉ liên quan đến dữ liệu trong bộ nhớ chính (RAM) hoặc bộ đệm (bộ đệm CPU) và thực hiện các tính toán trên hoặc với dữ liệu đó. Như vậy, giới hạn về các hoạt động này là tốc độ của CPU. Đây là lý do tại sao chúng tôi gọi chúng là các nhiệm vụ ràng buộc CPU.

Tính toán điểm trong một fractal.ThreadPoolExecutor that uses processes called the ProcessPoolExecutor that can be used for concurrency of CPU-bound tasks.

Ước tính PI

Bao thanh toán số nguyên tố.

Phân tích cú pháp HTML, JSON, vv Tài liệu.

Xử lý văn bản.

Chạy mô phỏng.ThreadPoolExecutor; they are:

  • CPU rất nhanh và chúng tôi thường có nhiều hơn một CPU. Chúng tôi muốn thực hiện các nhiệm vụ của mình và sử dụng đầy đủ nhiều lõi CPU trong phần cứng hiện đại.
  • Sử dụng các luồng và nhóm luồng thông qua lớp ThreadPoolExecutor trong Python có lẽ không phải là một con đường để đạt được kết thúc này.
  • Điều này là do một lý do kỹ thuật đằng sau cách mà thông dịch viên Python đã được thực hiện. Việc triển khai ngăn chặn hai hoạt động Python thực hiện cùng một lúc bên trong trình thông dịch và nó thực hiện điều này với một khóa chính mà chỉ có một luồng có thể giữ tại một thời điểm. Đây được gọi là khóa phiên dịch toàn cầu, hoặc Gil.

Gil không xấu xa và không bực bội; Đó là một quyết định thiết kế trong phiên dịch viên Python mà chúng ta phải nhận thức và xem xét trong việc thiết kế các ứng dụng của chúng tôi.

CPU rất nhanh và chúng tôi thường có nhiều hơn một CPU. Chúng tôi muốn thực hiện các nhiệm vụ của mình và sử dụng đầy đủ nhiều lõi CPU trong phần cứng hiện đại.

Sử dụng các luồng và nhóm luồng thông qua lớp ThreadPoolExecutor trong Python có lẽ không phải là một con đường để đạt được kết thúc này.ThreadPoolExecutor.

Điều này là do một lý do kỹ thuật đằng sau cách mà thông dịch viên Python đã được thực hiện. Việc triển khai ngăn chặn hai hoạt động Python thực hiện cùng một lúc bên trong trình thông dịch và nó thực hiện điều này với một khóa chính mà chỉ có một luồng có thể giữ tại một thời điểm. Đây được gọi là khóa phiên dịch toàn cầu, hoặc Gil.initializer” argument to specify the function name and “initargs” to specify a tuple of arguments to the function.

Gil không xấu xa và không bực bội; Đó là một quyết định thiết kế trong phiên dịch viên Python mà chúng ta phải nhận thức và xem xét trong việc thiết kế các ứng dụng của chúng tôi.

Tôi đã nói rằng bạn có lẽ không nên sử dụng các chủ đề cho các tác vụ gắn CPU.

Bạn có thể và được tự do làm như vậy, nhưng mã của bạn sẽ không được hưởng lợi từ sự đồng thời vì GIL. Nó có thể sẽ hoạt động tồi tệ hơn vì chi phí bổ sung của chuyển đổi ngữ cảnh (CPU nhảy từ một luồng thực thi này sang luồng thực thi khác) được giới thiệu bằng cách sử dụng các luồng.BrokenThreadPool exception.

Ngoài ra, GIL là một quyết định thiết kế ảnh hưởng đến việc triển khai tham chiếu của Python, mà bạn tải xuống từ python.org. Nếu bạn sử dụng một triển khai khác của trình thông dịch Python (chẳng hạn như Pypy, Ironpython, Jython và có lẽ các phiên bản khác), thì bạn có thể không phải tuân theo GIL và có thể sử dụng các luồng cho các tác vụ ràng buộc CPU trực tiếp.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

Python cung cấp một mô-đun đa bộ xử lý để thực hiện nhiệm vụ đa lõi cũng như anh chị em của ThreadPoolExecutor sử dụng các quy trình gọi là ProcessPoolExecutor có thể được sử dụng để đồng thời các nhiệm vụ liên quan đến CPU.

ThreadPoolExecutor Xử lý ngoại lệ

Xử lý ngoại lệ là một cân nhắc quan trọng khi sử dụng các chủ đề.time import sleep

Mã sẽ tăng một ngoại lệ khi một điều gì đó bất ngờ xảy ra và ngoại lệ nên được xử lý rõ ràng bởi ứng dụng của bạn, ngay cả khi nó có nghĩa là đăng nhập và tiếp tục.random import random

Các chủ đề Python rất phù hợp để sử dụng với các tác vụ ràng buộc IO và các hoạt động trong các tác vụ này thường tăng các ngoại lệ, chẳng hạn như nếu không thể đạt được máy chủ, nếu mạng đi xuống, nếu không thể tìm thấy tệp, v.v.threading import current_thread

Có ba điểm bạn có thể cần xem xét xử lý ngoại lệ khi sử dụng ThreadPoolExecutor; họ đang:concurrent.futures import ThreadPoolExecutor

# chức năng để khởi tạo luồng công nhân

DEF INSIVERIZER_Worker ():initializer_worker():

& nbsp; & nbsp; & nbsp; & nbsp;# nâng cao một ngoại lệ# raise an exception

& nbsp; & nbsp; & nbsp; & nbsp; nâng cao ngoại lệ ('một cái gì đó xấu đã xảy ra!')raise Exception('Something bad happened!')

# một nhiệm vụ giả ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây

Nhiệm vụ Def (Định danh):task(identifier):

    sleep(random())sleep(random())

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận tên duy nhất# get the unique name

    returnidentifierreturnidentifier

# Tạo một nhóm chủ đề

với ThreadPoolExecutor (MAX_Workers = 2, SititionIzer = initionizer_worker) AsexCutor:ThreadPoolExecutor(max_workers=2,initializer=initializer_worker)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực thi các tác vụ# execute tasks

& nbsp; & nbsp; & nbsp; & nbsp;forresult in executor.map(task,range(10)):

        print(result)print(result)

Chạy ví dụ thất bại với một ngoại lệ, như chúng tôi mong đợi.

Nhóm luồng được tạo theo bình thường, nhưng ngay khi chúng tôi cố gắng thực thi các tác vụ, các luồng công nhân mới được tạo, chức năng khởi tạo luồng công nhân tùy chỉnh được gọi và làm tăng một ngoại lệ.

Nhiều luồng đã cố gắng bắt đầu, và lần lượt, nhiều luồng không thành công với một ngoại lệ. Cuối cùng, bản thân nhóm chủ đề đã ghi lại một thông báo rằng hồ bơi bị hỏng và không thể được sử dụng nữa.

Ngoại lệ trong bộ khởi tạo:

Traceback (cuộc gọi gần đây nhất cuối cùng):

...

Nâng cao ngoại lệ ('Một cái gì đó xấu đã xảy ra!')

Ngoại lệ: Một cái gì đó xấu đã xảy ra!

Ngoại lệ trong bộ khởi tạo:

Traceback (cuộc gọi gần đây nhất cuối cùng):

...

Nâng cao ngoại lệ ('Một cái gì đó xấu đã xảy ra!')

Ngoại lệ: Một cái gì đó xấu đã xảy ra!

Traceback (cuộc gọi gần đây nhất cuối cùng):

...

Nâng cao ngoại lệ ('Một cái gì đó xấu đã xảy ra!')

Ngoại lệ: Một cái gì đó xấu đã xảy ra!

đồng thời

Điều này nhấn mạnh rằng nếu bạn sử dụng chức năng khởi tạo tùy chỉnh, bạn phải xem xét cẩn thận các ngoại lệ có thể được nêu ra và có thể xử lý chúng, nếu không thì có nguy cơ tất cả các nhiệm vụ phụ thuộc vào nhóm luồng.

Xử lý ngoại lệ trong quá trình thực hiện nhiệm vụFuture object associated with the task via the exception() function.

Một ngoại lệ có thể xảy ra trong khi thực hiện nhiệm vụ của bạn.result() in the Future in order to get the result. This will impact both calls to submit() and map() when adding tasks to the thread pool.

Điều này sẽ khiến nhiệm vụ ngừng thực thi, nhưng sẽ không phá vỡ nhóm luồng. Thay vào đó, ngoại lệ sẽ bị bắt bởi nhóm luồng và sẽ có sẵn thông qua đối tượng trong tương lai được liên kết với tác vụ thông qua hàm Exception ().

  • Thay vào đó, ngoại lệ sẽ được tăng lại nếu bạn gọi kết quả () trong tương lai để có được kết quả. Điều này sẽ tác động đến cả hai cuộc gọi để gửi () và MAP () khi thêm các tác vụ vào nhóm luồng.
  • Điều đó có nghĩa là bạn có hai tùy chọn để xử lý các ngoại lệ trong các nhiệm vụ; họ đang:

1. Xử lý các ngoại lệ trong hàm tác vụ.

2. Xử lý các ngoại lệ khi nhận được kết quả từ các nhiệm vụ.

Xử lý ngoại lệ trong nhiệm vụNone.

Xử lý ngoại lệ trong nhiệm vụ có nghĩa là bạn cần một số cơ chế để cho người nhận kết quả biết rằng điều gì đó bất ngờ đã xảy ra.

Điều này có thể thông qua giá trị trả về từ hàm, ví dụ: Không có.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

Ngoài ra, bạn có thể tăng một ngoại lệ và có người nhận xử lý trực tiếp. Tùy chọn thứ ba có thể là sử dụng một số trạng thái rộng hơn hoặc trạng thái toàn cầu hơn, có lẽ được truyền bằng cách tham chiếu vào cuộc gọi đến chức năng.

Ví dụ dưới đây xác định một nhiệm vụ công việc sẽ tăng một ngoại lệ, nhưng sẽ bắt được ngoại lệ và trả về kết quả cho thấy trường hợp thất bại.

# SuperfastPython.comtime import sleep

# Ví dụ về việc xử lý việc tăng ngoại lệ trong một nhiệm vụconcurrent.futures import ThreadPoolExecutor

từ thời gian nhập vào giấc ngủ

từ đồng thời.work():

    sleep(1)sleep(1)

    try:try:

# Nhiệm vụ giả sẽ ngủ trong giây látraise Exception('Something bad happened!')

def work ():except Exception:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;return'Unable to get the result'

& nbsp; & nbsp; & nbsp; & nbsp; ngoại trừ ngoại lệ:return "never gets here"

# Tạo một nhóm chủ đề

với ThreadPoolExecutor (MAX_Workers = 2, SititionIzer = initionizer_worker) AsexCutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực thi các tác vụ# execute our task

    future=executor.submit(work)future= executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp;# get the result from the task

    result=future.result()result=future.result()

    print(result)print(result)

Chạy ví dụ thất bại với một ngoại lệ, như chúng tôi mong đợi.

Nhóm luồng được tạo theo bình thường, nhưng ngay khi chúng tôi cố gắng thực thi các tác vụ, các luồng công nhân mới được tạo, chức năng khởi tạo luồng công nhân tùy chỉnh được gọi và làm tăng một ngoại lệ.

Nhiều luồng đã cố gắng bắt đầu, và lần lượt, nhiều luồng không thành công với một ngoại lệ. Cuối cùng, bản thân nhóm chủ đề đã ghi lại một thông báo rằng hồ bơi bị hỏng và không thể được sử dụng nữa.submit() and map(). It may require special handling of a custom return value for the failure case.

Ngoại lệ trong bộ khởi tạo:

Traceback (cuộc gọi gần đây nhất cuối cùng):

...

Điều đó có nghĩa là người nhận phải nhận thức được các loại lỗi mà nhiệm vụ có thể nêu ra và xử lý chúng một cách rõ ràng.

Ví dụ dưới đây xác định một nhiệm vụ đơn giản làm tăng một ngoại lệ, sau đó được người nhận xử lý khi cố gắng nhận kết quả từ cuộc gọi chức năng.Exception, which is then handled by the recipient when attempting to get the result from the function call.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

# SuperfastPython.com

# Ví dụ về việc xử lý một ngoại lệ được nêu trong một nhiệm vụ

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work():

    sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp; nâng cao ngoại lệ ('một cái gì đó xấu đã xảy ra!')raise Exception('Something bad happened!')

& nbsp; & nbsp; & nbsp; & nbsp; return "không bao giờ đến đây"return"never gets here"

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực hiện nhiệm vụ của chúng tôi# execute our task

    future=executor.submit(work)future=executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả từ nhiệm vụ# get the result from the task

    try:try:

        result=future.result()result =future.result()

& nbsp; & nbsp; & nbsp; & nbsp; ngoại trừ ngoại lệ:except Exception:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print('Unable to get the result')

Chạy ví dụ tạo ra nhóm luồng và gửi công việc theo bình thường. Nhiệm vụ không thành công với một lỗi, nhóm luồng bắt được ngoại lệ, lưu trữ nó, sau đó tái lập nó khi chúng tôi gọi hàm kết quả () trong tương lai.result() function in the Future.

Người nhận kết quả chấp nhận ngoại lệ và bắt nó, báo cáo một trường hợp thất bại.

Chúng ta cũng có thể kiểm tra ngoại lệ trực tiếp thông qua chức năng Call to the Exception () trên đối tượng tương lai. Hàm này chặn cho đến khi một ngoại lệ xảy ra và mất thời gian chờ, giống như một cuộc gọi để kết quả ().exception() function on the Future object. This function blocks until an exception occurs and takes a timeout, just like a call to result().

Nếu một ngoại lệ không bao giờ xảy ra và nhiệm vụ bị hủy hoặc hoàn thành thành công, thì Exception () sẽ trả về giá trị không có.exception() will return a value of None.

Chúng tôi có thể chứng minh việc kiểm tra rõ ràng cho một trường hợp đặc biệt trong nhiệm vụ trong ví dụ dưới đây.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# SuperfastPython.com

# Ví dụ về việc xử lý một ngoại lệ được nêu trong một nhiệm vụ

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work():

    sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp; nâng cao ngoại lệ ('một cái gì đó xấu đã xảy ra!')raise Exception('Something bad happened!')

& nbsp; & nbsp; & nbsp; & nbsp; return "không bao giờ đến đây"return"never gets here"

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực hiện nhiệm vụ của chúng tôi# execute our task

    future=executor.submit(work)future=executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả từ nhiệm vụ# get the result from the task

    exception=future.exception()exception= future.exception()

& nbsp; & nbsp; & nbsp; & nbsp; ngoại trừ ngoại lệ:# handle exceptional case

    ifexception:ifexception:

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print(exception)

    else:else:

        result=future.result()result =future.result()

        print(result)print(result)

Chạy ví dụ tạo ra nhóm luồng và gửi công việc theo bình thường. Nhiệm vụ không thành công với một lỗi, nhóm luồng bắt được ngoại lệ, lưu trữ nó, sau đó tái lập nó khi chúng tôi gọi hàm kết quả () trong tương lai.

Người nhận kết quả chấp nhận ngoại lệ và bắt nó, báo cáo một trường hợp thất bại.

Chúng ta cũng có thể kiểm tra ngoại lệ trực tiếp thông qua chức năng Call to the Exception () trên đối tượng tương lai. Hàm này chặn cho đến khi một ngoại lệ xảy ra và mất thời gian chờ, giống như một cuộc gọi để kết quả ().Future object for each task, as map() does not provide access to Future objects.

Nếu một ngoại lệ không bao giờ xảy ra và nhiệm vụ bị hủy hoặc hoàn thành thành công, thì Exception () sẽ trả về giá trị không có.map() to submit tasks, unless you wrap the entire iteration.

Chúng tôi có thể chứng minh việc kiểm tra rõ ràng cho một trường hợp đặc biệt trong nhiệm vụ trong ví dụ dưới đây.map() that happens to raise an Exception.

& nbsp; & nbsp; & nbsp; & nbsp;# xử lý trường hợp đặc biệt

# SuperfastPython.com

# Ví dụ về việc xử lý một ngoại lệ được nêu trong một nhiệm vụ

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ giả sẽ ngủ trong giây lát

def work ():work(value):

    sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp; nâng cao ngoại lệ ('một cái gì đó xấu đã xảy ra!')raise Exception('Something bad happened!')

& nbsp; & nbsp; & nbsp; & nbsp; return "không bao giờ đến đây"returnf'Never gets here {value}'

# Tạo một nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# thực hiện nhiệm vụ của chúng tôi# execute our task

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả từ nhiệm vụforresult inexecutor.map(work, [1]):

        print(result)print(result)

& nbsp; & nbsp; & nbsp; & nbsp; ngoại trừ ngoại lệ:map()) and waits for the first result.

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;

Chạy ví dụ tạo ra nhóm luồng và gửi công việc theo bình thường. Nhiệm vụ không thành công với một lỗi, nhóm luồng bắt được ngoại lệ, lưu trữ nó, sau đó tái lập nó khi chúng tôi gọi hàm kết quả () trong tương lai.

Người nhận kết quả chấp nhận ngoại lệ và bắt nó, báo cáo một trường hợp thất bại.

Chúng ta cũng có thể kiểm tra ngoại lệ trực tiếp thông qua chức năng Call to the Exception () trên đối tượng tương lai. Hàm này chặn cho đến khi một ngoại lệ xảy ra và mất thời gian chờ, giống như một cuộc gọi để kết quả ().

Nếu một ngoại lệ không bao giờ xảy ra và nhiệm vụ bị hủy hoặc hoàn thành thành công, thì Exception () sẽ trả về giá trị không có.

Chúng tôi có thể chứng minh việc kiểm tra rõ ràng cho một trường hợp đặc biệt trong nhiệm vụ trong ví dụ dưới đây.map() is used to submit tasks to the thread pool, then the tasks should handle their own exceptions or be simple enough that exceptions are not expected.

& nbsp; & nbsp; & nbsp; & nbsp;# xử lý trường hợp đặc biệt

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; in (ngoại lệ)ThreadPoolExecutor is in callback functions.

Khi phát hành các tác vụ cho nhóm luồng với một cuộc gọi để gửi (), chúng tôi sẽ nhận được một đối tượng trong tương lai để trả lại cho chúng tôi có thể đăng ký các chức năng gọi lại để gọi khi tác vụ hoàn thành thông qua hàm add_done_callback ().submit(), we receive a Future object in return on which we can register callback functions to call when the task completes via the add_done_callback() function.

Điều này cho phép một hoặc nhiều chức năng gọi lại được đăng ký sẽ được thực thi theo thứ tự chúng được đăng ký.

Các cuộc gọi lại này luôn được gọi, ngay cả khi tác vụ bị hủy hoặc không thể vượt qua một ngoại lệ.

Một cuộc gọi lại có thể thất bại với một ngoại lệ và nó sẽ không ảnh hưởng đến các chức năng gọi lại khác đã được đăng ký hoặc nhiệm vụ.

Ngoại lệ được bắt bởi nhóm luồng, đăng nhập như một thông báo loại ngoại lệ và thủ tục di chuyển. Theo một nghĩa nào đó, các cuộc gọi lại có thể thất bại trong âm thầm.

Chúng tôi có thể chứng minh điều này với một ví dụ hoạt động với nhiều chức năng gọi lại, trong đó đầu tiên sẽ tăng một ngoại lệ.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

# SuperfastPython.com

# Thêm các cuộc gọi lại vào tương lai, một trong số đó làm tăng một ngoại lệ

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

từ đồng thời.concurrent.futures import wait

# chức năng gọi lại để gọi khi hoàn thành tác vụ

def Custom_callback1 (Tương lai):custom_callback1(future):

& nbsp; & nbsp; & nbsp; & nbsp; nâng cao ngoại lệ ('một cái gì đó xấu đã xảy ra!')raise Exception('Something bad happened!')

& nbsp; & nbsp; & nbsp; & nbsp;# không bao giờ đến đây# never gets here

& nbsp; & nbsp; & nbsp; & nbsp; in ('gọi lại 1 được gọi.')print('Callback 1 called.')

# chức năng gọi lại để gọi khi hoàn thành tác vụ

def Custom_callback1 (Tương lai):custom_callback2(future):

& nbsp; & nbsp; & nbsp; & nbsp; nâng cao ngoại lệ ('một cái gì đó xấu đã xảy ra!')print('Callback 2 called.')

& nbsp; & nbsp; & nbsp; & nbsp;# không bao giờ đến đây

& nbsp; & nbsp; & nbsp; & nbsp; in ('gọi lại 1 được gọi.')work():

    sleep(1)sleep(1)

def Custom_callback2 (Tương lai):return'Task is done'

& nbsp; & nbsp; & nbsp; & nbsp; in ('gọi lại 2 được gọi.')

# Nhiệm vụ giả sẽ ngủ trong giây látThreadPoolExecutor()asexecutor:

def work ():# execute the task

    future=executor.submit(work)future= executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp; return'task đã hoàn thành '# add the custom callbacks

    future.add_done_callback(custom_callback1)future.add_done_callback(custom_callback1)

    future.add_done_callback(custom_callback2)future.add_done_callback(custom_callback2)

# Tạo một nhóm chủ đề# wait for the task to complete and get the result

    result=future.result()result=future.result()

với ThreadPoolExecutor () Asexecutor:# wait for callbacks to finish

    sleep(0.1)sleep(0.1)

    print(result)print(result)

& nbsp; & nbsp; & nbsp; & nbsp;# thực hiện nhiệm vụ

& nbsp; & nbsp; & nbsp; & nbsp;# Thêm các cuộc gọi lại tùy chỉnh

& nbsp; & nbsp; & nbsp; & nbsp;# chờ cho nhiệm vụ hoàn thành và nhận kết quả

& nbsp; & nbsp; & nbsp; & nbsp;# chờ gọi lại để hoàn thành

Chạy ví dụ bắt đầu nhóm luồng theo bình thường và thực hiện nhiệm vụ.

Khi nhiệm vụ hoàn thành, cuộc gọi lại đầu tiên được gọi, không thành công với một ngoại lệ. Ngoại lệ được ghi lại và báo cáo trên bảng điều khiển (hành vi mặc định để ghi nhật ký).

Nhóm chủ đề không bị hỏng và tiếp tục.

Cuộc gọi lại thứ hai được gọi là thành công, và cuối cùng, luồng chính nhận được kết quả của nhiệm vụ.

Gọi gọi điện ngoại lệ cho

Traceback (cuộc gọi gần đây nhất cuối cùng):

...

Nâng cao ngoại lệ ('Một cái gì đó xấu đã xảy ra!')

Ngoại lệ: Một cái gì đó xấu đã xảy ra!

Gọi lại 2 được gọi.ThreadPoolExecutor works internally.

Nhiệm vụ được thực hiện

Điều này nhấn mạnh rằng nếu các cuộc gọi lại dự kiến ​​sẽ tăng một ngoại lệ, thì nó phải được xử lý rõ ràng và kiểm tra xem bạn có muốn có tác động đến chính nhiệm vụ hay không.

Làm thế nào để ThreadPoolExecutor hoạt động trong nội bộThreadPoolExecutor and the base class here:

  • cpython/Lib/concurrent/futures/thread.py
  • cpython/Lib/concurrent/futures/_base.py

Điều quan trọng là phải tạm dừng trong một khoảnh khắc và xem xét cách thức hoạt động của ThreadPoolExecutor trong nội bộ.

Các hoạt động nội bộ của lớp tác động đến cách chúng ta sử dụng nhóm luồng và hành vi chúng ta có thể mong đợi, cụ thể xung quanh việc hủy bỏ các nhiệm vụ.

Không có kiến ​​thức này, một số hành vi của nhóm chủ đề có thể xuất hiện khó hiểu từ bên ngoài.

Bạn có thể thấy mã nguồn cho ThreadPoolExecutor và lớp cơ sở tại đây:

Có rất nhiều thứ chúng ta có thể tìm hiểu về cách nhóm chủ đề hoạt động trong nội bộ, nhưng chúng ta sẽ giới hạn bản thân ở các khía cạnh quan trọng nhất.

Có hai khía cạnh mà bạn cần xem xét về hoạt động nội bộ của lớp ThreadPoolExecutor: cách các tác vụ được gửi vào nhóm và cách tạo các luồng công nhân.SimpleQueue object, which is a thread-safe Queue implementation. This means we can add work to the pool from any thread and the Queue of work will not become corrupt from concurrent put() and get() operations.

Nhiệm vụ được thêm vào hàng đợi nội bộ

Các nhiệm vụ được gửi vào nhóm chủ đề bằng cách thêm chúng vào hàng đợi nội bộ.

Một nhiệm vụ được bọc trong một đối tượng bên trong gọi là _workItem. Điều này nắm bắt các chi tiết như hàm để gọi, các đối số, đối tượng tương lai liên quan và xử lý các ngoại lệ nếu chúng xảy ra trong quá trình thực hiện nhiệm vụ._WorkItem. This captures the details such as the function to call, the arguments, the associated Future object, and handling of exceptions if they occur during task execution.

Điều này giải thích làm thế nào một ngoại lệ trong một nhiệm vụ không làm giảm toàn bộ nhóm luồng, nhưng có thể được kiểm tra và truy cập sau khi nhiệm vụ hoàn thành.

Khi đối tượng _WorkItem được lấy từ hàng đợi bằng luồng công nhân, nó sẽ kiểm tra xem nhiệm vụ đã bị hủy trước khi nó được thực thi. Nếu vậy, nó sẽ quay lại ngay lập tức và không thực hiện nội dung của nhiệm vụ._WorkItem object is retrieved from the queue by a worker thread, it will check if the task has been cancelled before it is executed. If so, it will return immediately and not execute the content of the task.

Điều này giải thích nội bộ về cách hủy bỏ được thực hiện bởi nhóm luồng và lý do tại sao chúng ta không thể hủy một tác vụ đang chạy.

Chủ đề công nhân được tạo khi cần thiết

Chủ đề công nhân không được tạo khi nhóm luồng được tạo.

Thay vào đó, các chủ đề công nhân được tạo theo yêu cầu hoặc đúng lúc.

Mỗi lần một tác vụ được thêm vào hàng đợi bên trong, nhóm luồng sẽ kiểm tra xem số lượng luồng hoạt động có nhỏ hơn giới hạn trên của các luồng được hỗ trợ bởi nhóm luồng không. Nếu vậy, một chủ đề bổ sung được tạo để xử lý công việc mới.

Khi một chủ đề đã hoàn thành một nhiệm vụ, nó sẽ chờ đợi trên hàng đợi cho công việc mới đến. Khi công việc mới đến, tất cả các luồng đang chờ đợi trên hàng đợi sẽ được thông báo và người ta sẽ tiêu thụ đơn vị công việc và bắt đầu thực hiện nó.

Hai điểm này cho thấy cách nhóm sẽ chỉ tạo ra các chủ đề mới cho đến khi đạt được giới hạn và cách sử dụng các luồng, chờ đợi các tác vụ mới mà không tiêu thụ tài nguyên tính toán.

Nó cũng cho thấy nhóm chủ đề sẽ không phát hành luồng sau một số đơn vị công việc cố định. Có lẽ đây sẽ là một bổ sung tốt đẹp cho API trong tương lai.

Bây giờ chúng tôi đã hiểu cách thức hoạt động được đưa vào nhóm luồng và cách các nhóm quản lý các chủ đề, hãy để xem xét một số thực tiễn tốt nhất để xem xét khi sử dụng ThreadPoolExecutor.

ThreadPoolExecutor Thực tiễn tốt nhất

Bây giờ chúng ta đã biết cách thức hoạt động của ThreadPoolExecutor và cách sử dụng nó, hãy để xem xét một số thực tiễn tốt nhất để xem xét khi đưa các nhóm chủ đề vào các chương trình Python của chúng tôi.ThreadPoolExecutor works and how to use it, let’s review some best practices to consider when bringing thread pools into our Python programs.

Để giữ cho mọi thứ đơn giản, có năm thực hành tốt nhất; họ đang:

  • 1. Sử dụng Trình quản lý bối cảnh
  • 2. Sử dụng bản đồ () cho các vòng lặp không đồng bộ
  • 3. Sử dụng Gửi () với AS_Completed ()
  • 4. Sử dụng các chức năng độc lập làm nhiệm vụ
  • 5. Sử dụng cho các nhiệm vụ ràng buộc IO (có thể)

Sử dụng Trình quản lý bối cảnh

Sử dụng trình quản lý bối cảnh khi sử dụng nhóm luồng và xử lý tất cả các tác vụ gửi đến nhóm luồng và kết quả xử lý trong Trình quản lý.

Ví dụ:

.....

# Tạo nhóm chủ đề thông qua trình quản lý bối cảnh

với ThreadPoolExecutor (10) Asexecutor:ThreadPoolExecutor(10)asexecutor:

# ...

Hãy nhớ định cấu hình nhóm luồng của bạn khi tạo nó trong trình quản lý ngữ cảnh, cụ thể bằng cách đặt số lượng luồng để sử dụng trong nhóm.

Sử dụng trình quản lý bối cảnh tránh tình huống mà bạn đã khởi tạo rõ ràng nhóm luồng và quên tắt nó theo cách thủ công bằng cách gọi tắt ().shutdown().

Nó cũng ít mã hơn và được nhóm tốt hơn so với quản lý khởi tạo và tắt thủ công; Ví dụ:

.....

# Tạo nhóm chủ đề thông qua trình quản lý bối cảnh

executor=ThreadPoolExecutor(10)=ThreadPoolExecutor(10)

# ...

executor.shutdown().shutdown()

Hãy nhớ định cấu hình nhóm luồng của bạn khi tạo nó trong trình quản lý ngữ cảnh, cụ thể bằng cách đặt số lượng luồng để sử dụng trong nhóm.

Sử dụng trình quản lý bối cảnh tránh tình huống mà bạn đã khởi tạo rõ ràng nhóm luồng và quên tắt nó theo cách thủ công bằng cách gọi tắt ().

Nó cũng ít mã hơn và được nhóm tốt hơn so với quản lý khởi tạo và tắt thủ công; Ví dụ:map() function to dispatch the tasks asynchronously.

# Tạo một nhóm chủ đề theo cách thủ côngmyfunc() for each item:

.....

# Tạo nhóm chủ đề thông qua trình quản lý bối cảnh

với ThreadPoolExecutor (10) Asexecutor:item inmylist:

result=myfunc(item)=myfunc(item)

# ...

Hãy nhớ định cấu hình nhóm luồng của bạn khi tạo nó trong trình quản lý ngữ cảnh, cụ thể bằng cách đặt số lượng luồng để sử dụng trong nhóm.

.....

# Tạo nhóm chủ đề thông qua trình quản lý bối cảnh

với ThreadPoolExecutor (10) Asexecutor:result inmap(myfinc,mylist):

# ...

Hãy nhớ định cấu hình nhóm luồng của bạn khi tạo nó trong trình quản lý ngữ cảnh, cụ thể bằng cách đặt số lượng luồng để sử dụng trong nhóm.map() function on the thread pool.

.....

# Tạo nhóm chủ đề thông qua trình quản lý bối cảnh

với ThreadPoolExecutor (10) Asexecutor:result inexecutor.map(myfunc,mylist):

# ...

Hãy nhớ định cấu hình nhóm luồng của bạn khi tạo nó trong trình quản lý ngữ cảnh, cụ thể bằng cách đặt số lượng luồng để sử dụng trong nhóm.map() function if your target task function has side effects.

Sử dụng trình quản lý bối cảnh tránh tình huống mà bạn đã khởi tạo rõ ràng nhóm luồng và quên tắt nó theo cách thủ công bằng cách gọi tắt ().map() function if your target task function has no arguments or more than one argument.

Nó cũng ít mã hơn và được nhóm tốt hơn so với quản lý khởi tạo và tắt thủ công; Ví dụ:map() function if you need control over exception handling for each task, or if you would like to get results to tasks in the order that tasks are completed.

# Tạo một nhóm chủ đề theo cách thủ công

Don Tiết sử dụng trình quản lý bối cảnh khi bạn cần gửi các tác vụ và nhận kết quả trong bối cảnh rộng hơn (ví dụ: nhiều chức năng) và/hoặc khi bạn có quyền kiểm soát nhiều hơn đối với việc tắt nhóm.submit() and as_completed().

Hàm Substem () nằm trên nhóm luồng và được sử dụng để đẩy các tác vụ vào nhóm để thực thi và trả về ngay lập tức với một đối tượng trong tương lai cho nhiệm vụ. Hàm as_completed () là một phương thức mô -đun sẽ có thể sử dụng các đối tượng trong tương lai, như một danh sách và sẽ trả về các đối tượng trong tương lai khi các tác vụ được hoàn thành.submit() function is on the thread pool and is used to push tasks into the pool for execution and returns immediately with a Future object for the task. The as_completed() function is a module method that will take an iterable of Future objects, like a list, and will return Future objects as the tasks are completed.

Ví dụ:

.....

# Gửi tất cả các tác vụ và nhận các đối tượng trong tương lai

Tương lai = [Executor.Submit (MyFunc, Item) ForItem Inmylist]=[executor.submit(myfunc,item)foritem inmylist]

# Kết quả xử lý từ các nhiệm vụ theo thứ tự hoàn thành nhiệm vụ

forfuture inas_completed (tương lai):future inas_completed(futures):

# Nhận kết quả

result=future.result()=future.result()

# làm việc gì đó...

Không sử dụng kết hợp Sublication () và AS_Completed () nếu bạn cần xử lý kết quả theo thứ tự các tác vụ được gửi đến nhóm luồng.submit() and as_completed() combination if you need to process the results in the order that the tasks were submitted to the thread pool.

Không sử dụng kết hợp SOUSM () và AS_COMPLETED () nếu bạn cần kết quả từ tất cả các tác vụ để tiếp tục; Bạn có thể tốt hơn khi sử dụng hàm mô -đun Wait ().submit() and as_completed() combination if you need results from all tasks to continue; you may be better off using the wait() module function.

Không sử dụng kết hợp Subbor () và as_completed () cho một vòng lặp không đồng bộ đơn giản; Bạn có thể tốt hơn khi sử dụng Map ().submit() and as_completed() combination for a simple asynchronous for-loop; you may be better off using map().

Sử dụng các chức năng độc lập làm nhiệm vụ

Sử dụng ThreadPoolExecutor nếu các nhiệm vụ của bạn độc lập.ThreadPoolExecutor if your tasks are independent.

Điều này có nghĩa là mỗi nhiệm vụ không phụ thuộc vào các nhiệm vụ khác có thể thực thi cùng một lúc. Nó cũng có thể có nghĩa là các tác vụ không phụ thuộc vào bất kỳ dữ liệu nào ngoài dữ liệu được cung cấp thông qua các đối số chức năng cho tác vụ.

ThreadPoolExecutor là lý tưởng cho các tác vụ không thay đổi bất kỳ dữ liệu nào, ví dụ: Không có tác dụng phụ, được gọi là các chức năng thuần túy.ThreadPoolExecutor is ideal for tasks that do not change any data, e.g. have no side effects, so-called pure functions.

Các nhóm luồng có thể được tổ chức thành các luồng dữ liệu và đường ống cho sự phụ thuộc tuyến tính giữa các tác vụ, có lẽ với một nhóm luồng cho mỗi loại tác vụ.

Nhóm chủ đề không được thiết kế cho các nhiệm vụ yêu cầu phối hợp; Bạn nên xem xét sử dụng các lớp luồng và các mẫu phối hợp như rào cản và semaphore.Thread class and coordination patterns like the Barrier and Semaphore.

Các nhóm chủ đề không được thiết kế cho các nhiệm vụ yêu cầu đồng bộ hóa; Bạn nên xem xét sử dụng lớp luồng và các mẫu khóa như khóa và rlock.Thread class and locking patterns like Lock and RLock.

Sử dụng cho các nhiệm vụ ràng buộc IO (có thể)

Chỉ sử dụng ThreadPoolExecutor cho các tác vụ ràng buộc IO.ThreadPoolExecutor for IO-bound tasks only.

Đây là các tác vụ có thể liên quan đến việc tương tác với một thiết bị bên ngoài như ngoại vi (ví dụ: máy ảnh hoặc máy in), thiết bị lưu trữ (ví dụ: thiết bị lưu trữ hoặc ổ cứng) hoặc máy tính khác (ví dụ: giao tiếp ổ cắm).

Các luồng và nhóm chủ đề như ThreadPoolExecutor có lẽ không phù hợp với các tác vụ ràng buộc CPU, như tính toán trên dữ liệu trong bộ nhớ.ThreadPoolExecutor are not probably appropriate for CPU-bound tasks, like computation on data in memory.

Điều này là do các quyết định thiết kế trong trình thông dịch Python sử dụng khóa chính được gọi là khóa phiên dịch toàn cầu (GIL) ngăn chặn nhiều hơn một hướng dẫn Python thực hiện cùng một lúc.

Quyết định thiết kế này đã được đưa ra trong quá trình thực hiện tham chiếu của phiên dịch Python (từ Python.org), nhưng có thể không ảnh hưởng đến các phiên dịch viên khác (như Pypy, Iron Python và Jython).

Lỗi phổ biến khi sử dụng ThreadPoolExecutor

Có một số lỗi phổ biến khi sử dụng ThreadPoolExecutor.ThreadPoolExecutor.

Các lỗi này thường được thực hiện do các lỗi được giới thiệu bởi mã sao chép và thực hiện hoặc từ một sự hiểu lầm nhỏ trong cách hoạt động của ThreadPoolExecutor.ThreadPoolExecutor works.

Chúng tôi sẽ xem xét kỹ hơn một số lỗi phổ biến hơn khi sử dụng ThreadPoolExecutor.ThreadPoolExecutor.

Bạn có lỗi bằng cách sử dụng ThreadPoolExecutor không? Hãy cho tôi biết trong các bình luận để tôi có thể đề xuất sửa chữa và thêm trường hợp vào phần này.
Let me know in the comments so I can recommend a fix and add the case to this section.

Sử dụng một cuộc gọi chức năng trong SUMMT ()

Một lỗi phổ biến là gọi chức năng của bạn khi sử dụng chức năng SUBME ().submit() function.

Ví dụ:

.....

# Gửi nhiệm vụ

future=executor.submit(task())=executor.submit(task())

Một ví dụ hoàn chỉnh với lỗi này được liệt kê dưới đây.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

# SuperfastPython.com

# Ví dụ về cuộc gọi gọi gửi với một cuộc gọi chức năng

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

nhiệm vụ def ():task():

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# sleep for less than a second

    sleep(random())sleep(random())

& nbsp; & nbsp; & nbsp; & nbsp; return'all đã hoàn thành 'return'all done'

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi nhiệm vụ# submit the task

    future=executor.submit(task())future=executor.submit(task())

& nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả# get the result

    result=future.result()result=future.result()

    print(result)print(result)

Chạy ví dụ này sẽ thất bại với một lỗi.

Traceback (cuộc gọi gần đây nhất cuối cùng):

...

result = self.fn (*self.args, ** self.kwargs)

TypeError: đối tượng 'str' không thể gọi được

Bạn có thể sửa lỗi bằng cách cập nhật cuộc gọi để gửi () để lấy tên của chức năng của bạn và bất kỳ đối số nào, thay vì gọi chức năng trong cuộc gọi để gửi.submit() to take the name of your function and any arguments, instead of calling the function in the call to submit.

Ví dụ:

.....

# Gửi nhiệm vụ

future=executor.submit(task)=executor.submit(task)

Sử dụng một cuộc gọi chức năng trong bản đồ ()

Một lỗi phổ biến là gọi hàm của bạn khi sử dụng hàm map ().map() function.

Ví dụ:

.....

# Gửi tất cả các nhiệm vụ

Forresult Un tasecutor.map (Task (), phạm vi (5)):result inexecutor.map(task(),range(5)):

    print(result)print(result)

Một ví dụ hoàn chỉnh với lỗi này được liệt kê dưới đây.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

# SuperfastPython.com

# Ví dụ về bản đồ gọi với cuộc gọi chức năng

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

nhiệm vụ def (giá trị):task(value):

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# sleep for less than a second

    sleep(random())sleep(random())

    returnvaluereturnvalue

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi tất cả các nhiệm vụ# submit all tasks

& nbsp; & nbsp; & nbsp; & nbsp;forresult inexecutor.map(task(), range(5)):

        print(result)print(result)

Chạy ví dụ kết quả trong một kiểu mẫuTypeError

Traceback (cuộc gọi gần đây nhất cuối cùng):

...

# Gửi tất cả các nhiệm vụ

Forresult Un tasecutor.map (Task (), phạm vi (5)):map() to pass the name of the target task function instead of a call to the function.

.....

# Gửi tất cả các nhiệm vụ

Forresult Un tasecutor.map (Task (), phạm vi (5)):result inexecutor.map(task,range(5)):

    print(result)print(result)

Một ví dụ hoàn chỉnh với lỗi này được liệt kê dưới đây.

# SuperfastPython.commap() is to provide no second argument to function, e.g. the iterable.

Ví dụ:

.....

# Gửi tất cả các nhiệm vụ

Forresult Un tasecutor.map (Task (), phạm vi (5)):result inexecutor.map(task):

    print(result)print(result)

Một ví dụ hoàn chỉnh với lỗi này được liệt kê dưới đây.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

# SuperfastPython.com

# Ví dụ về bản đồ gọi với cuộc gọi chức năng

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổi

nhiệm vụ def (giá trị):task(value):

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây# sleep for less than a second

    sleep(random())sleep(random())

    returnvaluereturnvalue

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi tất cả các nhiệm vụ# submit all tasks

& nbsp; & nbsp; & nbsp; & nbsp;forresult inexecutor.map(task):

        print(result)print(result)

Chạy ví dụ kết quả trong một kiểu mẫumap() function to iterate over.

Traceback (cuộc gọi gần đây nhất cuối cùng):

TypeError: Task () Thiếu 1 Đối số vị trí bắt buộc: 'Giá trị'map() along with your function name.

.....

# Gửi tất cả các nhiệm vụ

Forresult Un tasecutor.map (Task (), phạm vi (5)):result inexecutor.map(task,range(5)):

    print(result)print(result)

Một ví dụ hoàn chỉnh với lỗi này được liệt kê dưới đây.

# SuperfastPython.comFuture in the signature for the callback function registered with a Future object.

Ví dụ:

.....

# Ví dụ về bản đồ gọi với cuộc gọi chức năng

từ thời gian nhập vào giấc ngủcustom_callback():

từ nhập ngẫu nhiên ngẫu nhiênprint('Custom callback was called')

Một ví dụ hoàn chỉnh với lỗi này được liệt kê dưới đây.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# SuperfastPython.com

# Ví dụ về bản đồ gọi với cuộc gọi chức năng

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Ví dụ về bản đồ gọi với cuộc gọi chức năng

từ thời gian nhập vào giấc ngủcustom_callback():

từ nhập ngẫu nhiên ngẫu nhiênprint('Custom callback was called')

từ đồng thời.

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổiwork():

    sleep(1)sleep(1)

nhiệm vụ def (giá trị):return 'Task is done'

& nbsp; & nbsp; & nbsp; & nbsp;# ngủ ít hơn một giây

với ThreadPoolExecutor () Asexecutor:ThreadPoolExecutor()asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# Gửi tất cả các nhiệm vụ# execute the task

    future=executor.submit(work)future= executor.submit(work)

& nbsp; & nbsp; & nbsp; & nbsp;# add the custom callback

    future.add_done_callback(custom_callback)future.add_done_callback(custom_callback)

Chạy ví dụ kết quả trong một kiểu mẫu# get the result

    result=future.result()result =future.result()

    print(result)print(result)

Traceback (cuộc gọi gần đây nhất cuối cùng):

TypeError: Task () Thiếu 1 Đối số vị trí bắt buộc: 'Giá trị'

Lỗi này có thể được sửa bằng cách thay đổi lệnh gọi đến map () để chuyển tên của hàm tác vụ đích thay vì gọi đến hàm.

Traceback (cuộc gọi gần đây nhất cuối cùng):

...

TypeError: Task () Thiếu 1 Đối số vị trí bắt buộc: 'Giá trị'

Lỗi này có thể được sửa bằng cách thay đổi lệnh gọi đến map () để chuyển tên của hàm tác vụ đích thay vì gọi đến hàm.Future object.

.....

# Ví dụ về bản đồ gọi với cuộc gọi chức năng

từ thời gian nhập vào giấc ngủcustom_callback(future):

từ nhập ngẫu nhiên ngẫu nhiênprint('Custom callback was called')

từ đồng thời.

# Nhiệm vụ tùy chỉnh sẽ ngủ trong một khoảng thời gian thay đổiThreadPoolExecutor.

nhiệm vụ def (giá trị):
Ask your question in the comments below and I will do my best to answer it and perhaps add it to this list of questions.

Làm thế nào để bạn dừng một nhiệm vụ đang chạy?

Một nhiệm vụ trong ThreadPoolExecutor có thể bị hủy trước khi nó bắt đầu chạy.ThreadPoolExecutor can be cancelled before it has started running.

Trong trường hợp này, nhiệm vụ phải được gửi vào nhóm bằng cách gọi gửi (), trả về một đối tượng trong tương lai. Sau đó, bạn có thể gọi hàm hủy () trong tương lai.submit(), which returns a Future object. You can then call the cancel() function in the future.

Nếu nhiệm vụ đã chạy, nó không thể bị hủy, dừng hoặc chấm dứt bởi nhóm luồng.

Thay vào đó, bạn phải thêm chức năng này vào nhiệm vụ của bạn.

Một cách tiếp cận có thể là sử dụng một lá cờ an toàn luồng, như một luồng.Event rằng, nếu được đặt, sẽ chỉ ra rằng tất cả các tác vụ phải ngừng chạy ngay khi chúng có thể. Sau đó, bạn có thể cập nhật chức năng hoặc chức năng tác vụ mục tiêu của mình để kiểm tra trạng thái của cờ này thường xuyên.threading.Event that, if set, will indicate that all tasks must stop running as soon as they can. You can then update your target task function or functions to check the state of this flag frequently.

Nó có thể yêu cầu bạn thay đổi cấu trúc của nhiệm vụ của bạn.

Ví dụ: nếu tác vụ của bạn đọc dữ liệu từ tệp hoặc ổ cắm, bạn có thể cần thay đổi thao tác đọc để được thực hiện trong các khối dữ liệu trong một vòng lặp để mỗi lần lặp của vòng lặp bạn có thể kiểm tra trạng thái của cờ.

Ví dụ dưới đây cung cấp một mẫu bạn có thể sử dụng để thêm cờ sự kiện vào chức năng tác vụ mục tiêu của mình để kiểm tra điều kiện dừng để tắt tất cả các tác vụ đang chạy hiện đang chạy.

Ví dụ dưới đây cho thấy điều này với một ví dụ làm việc.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

# SuperfastPython.com

# Ví dụ về việc dừng các tác vụ chạy bằng cách sử dụng một sự kiện

từ thời gian nhập vào giấc ngủtime import sleep

Từ sự kiện nhập luồngthreading import Event

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Chức năng tác vụ mục tiêu giả

Công việc def (sự kiện):work(event):

& nbsp; & nbsp; & nbsp; & nbsp;# giả vờ đọc dữ liệu trong một thời gian dài# pretend read data for a long time

    for_inrange(100):for_inrange(100):

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# pretend to read some data

        sleep(1)sleep(1)

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;## check the status of the flag

        ifevent.is_set():ifevent.is_set():

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# shut down this task now

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;print("Not done, asked to stop")

            returnreturn

& nbsp; & nbsp; & nbsp; & nbsp; trả lại "Tất cả đã hoàn thành!"return"All done!"

# Tạo một sự kiện để tắt tất cả các tác vụ đang chạy

event=Event()=Event()

# Tạo một nhóm chủ đề

executor=ThreadPoolExecutor(5) =ThreadPoolExecutor(5)

# Thực hiện tất cả các nhiệm vụ của chúng tôi

futures=[executor.submit(work,event)for_inrange(50)]=[executor.submit(work,event)for_in range(50)]

# đợi một chút

in ('Nhiệm vụ đang chạy ...')('Tasks are running...')

sleep(2)(2)

# Hủy tất cả các nhiệm vụ theo lịch trình

In ('Hủy tất cả các nhiệm vụ theo lịch trình ...')('Cancelling all scheduled tasks...')

Infutures forfuture:future in futures:

    future.cancel()future.cancel()

# dừng tất cả các nhiệm vụ hiện đang chạy

in ('Trigger tất cả các tác vụ đang chạy để dừng ...')('Trigger all running tasks to stop...')

event.set().set()

# Tắt nhóm chủ đề và chờ hoàn thành tất cả các nhiệm vụ

In ('Tắt xuống ...')('Shutting down...')

executor.shutdown().shutdown()

Chạy ví dụ đầu tiên tạo ra một nhóm chủ đề với 5 luồng công nhân và lịch trình 50 nhiệm vụ.

Một đối tượng sự kiện được tạo và chuyển cho mỗi nhiệm vụ nơi nó được kiểm tra từng lần lặp để xem nó đã được đặt và nếu có để bảo lãnh ra khỏi nhiệm vụ.

5 nhiệm vụ đầu tiên bắt đầu thực hiện trong vài giây, sau đó chúng tôi quyết định tắt mọi thứ.

Đầu tiên, chúng tôi hủy tất cả các tác vụ theo lịch trình chưa chạy để nếu họ biến nó thành hàng đợi thành một chuỗi công nhân, họ sẽ không bắt đầu chạy.

Sau đó, chúng tôi đánh dấu đặt sự kiện để kích hoạt tất cả các tác vụ đang chạy để dừng.

Nhóm chủ đề sau đó được tắt và chúng tôi chờ tất cả các luồng chạy để hoàn thành việc thực thi của họ.

Năm chủ đề đang chạy kiểm tra trạng thái của sự kiện trong vòng lặp vòng lặp tiếp theo của họ và bảo lãnh, in một tin nhắn.

Nhiệm vụ đang chạy ...

Hủy bỏ tất cả các nhiệm vụ theo lịch trình ...

Kích hoạt tất cả các tác vụ đang chạy để dừng ...

Đang Tắt...

Chưa hoàn thành, được yêu cầu dừng lại

Chưa hoàn thành, được yêu cầu dừng lại

Chưa hoàn thành, được yêu cầu dừng lại

Chưa hoàn thành, được yêu cầu dừng lại

Chưa hoàn thành, được yêu cầu dừng lại

Làm thế nào để bạn chờ đợi tất cả các nhiệm vụ hoàn thành?

Có một vài cách để chờ tất cả các nhiệm vụ hoàn thành trong một ThreadPoolExecutor.ThreadPoolExecutor.

Đầu tiên, nếu bạn có một đối tượng trong tương lai cho tất cả các tác vụ trong nhóm luồng vì bạn đã gọi là Substem (), thì bạn có thể cung cấp bộ sưu tập các tác vụ cho hàm mô -đun chờ (). Theo mặc định, chức năng này sẽ trở lại khi tất cả các đối tượng được cung cấp đã hoàn thành.Future object for all tasks in the thread pool because you called submit(), then you can provide the collection of tasks to the wait() module function. By default, this function will return when all provided Future objects have completed.

.....

# Đợi tất cả các nhiệm vụ hoàn thành

wait(futures)(futures)

Ngoài ra, bạn có thể liệt kê danh sách các đối tượng trong tương lai và cố gắng nhận kết quả từ mỗi đối tượng. Việc lặp lại này sẽ hoàn thành khi tất cả các kết quả có sẵn có nghĩa là tất cả các nhiệm vụ đã được hoàn thành.Future objects and attempt to get the result from each. This iteration will complete when all results are available meaning that all tasks were completed.

.....

# Đợi tất cả các nhiệm vụ hoàn thành

Infutures forfuture:future infutures:

result=future.result()=future.result()

# dừng tất cả các nhiệm vụ hiện đang chạy

in ('Trigger tất cả các tác vụ đang chạy để dừng ...')cancel_futures” to True, which will cancel all scheduled tasks and wait for all currently running tasks to complete.

.....

# Tắt máy nhóm, hủy bỏ các nhiệm vụ theo lịch trình, trả lại khi hoàn thành các tác vụ

executor.shutdown(wait=True,cancel_futures=True).shutdown(wait=True,cancel_futures=True)

Bạn cũng có thể đóng cửa hồ bơi và không hủy bỏ các nhiệm vụ theo lịch trình, nhưng vẫn chờ tất cả các nhiệm vụ hoàn thành. Điều này sẽ đảm bảo tất cả các nhiệm vụ chạy và theo lịch trình được hoàn thành trước khi chức năng trả về. Đây là hành vi mặc định của hàm tắt (), nhưng là một ý tưởng tốt để chỉ định rõ ràng.shutdown() function, but is a good idea to specify explicitly.

.....

# Tắt máy nhóm, trả lại sau khi hoàn thành tất cả các nhiệm vụ theo lịch và chạy

executor.shutdown(wait=True,cancel_futures=False).shutdown(wait=True,cancel_futures=False)

Làm thế nào để bạn tự động thay đổi số lượng chủ đề?

Bạn không thể tự động tăng hoặc giảm số lượng chủ đề trong ThreadPoolExecutor.ThreadPoolExecutor.

Số lượng luồng được cố định khi ThreadPoolExecutor được cấu hình trong lệnh gọi đến Trình xây dựng đối tượng. Ví dụ:ThreadPoolExecutor is configured in the call to the object constructor. For example:

.....

# Định cấu hình một nhóm chủ đề

executor=ThreadPoolExecutor(20)=ThreadPoolExecutor(20)

Làm thế nào để bạn đăng nhập từ một nhiệm vụ?

Các chức năng tác vụ mục tiêu của bạn được thực hiện bởi các luồng công nhân trong nhóm luồng và bạn có thể lo ngại liệu việc đăng nhập từ các chức năng tác vụ đó có an toàn cho luồng hay không.

Đó là, nhật ký sẽ bị hỏng nếu hai luồng cố gắng cùng một lúc. Câu trả lời là không, nhật ký sẽ không bị hỏng.

Chức năng ghi nhật ký Python là an toàn theo luồng theo mặc định.

Ví dụ: xem trích dẫn này từ tài liệu API mô -đun đăng nhập:

Mô-đun ghi nhật ký được dự định là an toàn chủ đề mà không cần bất kỳ công việc đặc biệt nào cần được thực hiện bởi các máy khách của mình. Nó đạt được điều này mặc dù sử dụng khóa luồng; Có một khóa để tuần tự hóa quyền truy cập vào mô -đun dữ liệu được chia sẻ và mỗi trình xử lý cũng tạo một khóa để tuần tự hóa quyền truy cập vào I/O cơ bản của nó.

- Cơ sở ghi nhật ký cho Python, an toàn chủ đề.

Do đó, bạn có thể đăng nhập từ các chức năng tác vụ mục tiêu của mình trực tiếp.

Làm thế nào để bạn các nhiệm vụ đơn vị và nhóm chủ đề?

Bạn có thể trực tiếp kiểm tra các chức năng tác vụ mục tiêu của mình, có thể chế giễu mọi tài nguyên bên ngoài cần thiết.

Bạn có thể kiểm tra đơn vị sử dụng nhóm luồng của bạn với các tác vụ giả không tương tác với các tài nguyên bên ngoài.

Kiểm tra đơn vị các tác vụ và bản thân nhóm luồng phải được coi là một phần của thiết kế của bạn và có thể yêu cầu kết nối đó với tài nguyên IO có thể định cấu hình để nó có thể bị chế giễu và chức năng tác vụ đích được gọi là nhóm chủ đề của bạn có thể cấu hình để có thể định cấu hình Nó có thể bị chế giễu.

Làm thế nào để bạn so sánh hiệu suất nối tiếp với hiệu suất song song?

Bạn có thể so sánh hiệu suất của chương trình của bạn có và không có nhóm chủ đề.

Đây có thể là một bài tập hữu ích để xác nhận rằng việc sử dụng ThreadPoolExecutor trong chương trình của bạn đã dẫn đến tăng tốc.ThreadPoolExecutor in your program has resulted in a speed-up.

Có lẽ cách tiếp cận đơn giản nhất là ghi lại thủ công thời gian bắt đầu và kết thúc của mã của bạn và trừ kết thúc từ thời gian bắt đầu để báo cáo tổng thời gian thực hiện. Sau đó ghi lại thời gian có và không có việc sử dụng nhóm luồng.

# SuperfastPython.com

# Ví dụ về ghi lại thời gian thực hiện của một chương trình

Thời gian nhập khẩutime

# Ghi lại thời gian bắt đầu

start_time=time.time()=time.time()

# làm việc với hoặc không có nhóm chủ đề

# ....

time.sleep(3).sleep(3)

# Ghi lại thời gian kết thúc

end_time=time.time()=time.time()

# Báo cáo thời gian thực hiện

total_time=end_time-start_time=end_time-start_time

print (f'Executing thời gian: {Total_time: .1f} giây. ')(f'Execution time: {total_time:.1f} seconds.')

Sử dụng thời gian thực hiện chương trình trung bình có thể cung cấp thời gian chương trình ổn định hơn so với chạy một lần.

Bạn có thể ghi lại thời gian thực hiện 3 lần trở lên cho chương trình của mình mà không cần nhóm luồng sau đó tính trung bình khi tổng thời gian chia cho tổng số lần chạy. Sau đó lặp lại bài tập này để tính thời gian trung bình với nhóm luồng.

Điều này có thể chỉ phù hợp nếu thời gian chạy của chương trình của bạn là vài phút thay vì hàng giờ.

Sau đó, bạn có thể so sánh phiên bản nối tiếp so với song song bằng cách tính toán tốc độ tăng lên nhiều như:

  • Tăng tốc độ nhiều = thời gian nối tiếp / thời gian song song

Ví dụ: nếu lần chạy nối tiếp của một chương trình mất 15 phút (900 giây) và phiên bản song song với một ThreadPoolExecutor mất 5 phút (300 giây), thì tỷ lệ phần trăm sẽ được tính như:ThreadPoolExecutor took 5 minutes (300 seconds), then the percentage multiple up would be calculated as:

  • Tăng tốc độ nhiều = thời gian nối tiếp / thời gian song song
  • Ví dụ: nếu lần chạy nối tiếp của một chương trình mất 15 phút (900 giây) và phiên bản song song với một ThreadPoolExecutor mất 5 phút (300 giây), thì tỷ lệ phần trăm sẽ được tính như:
  • Tăng tốc độ nhiều = 900/300

Tăng tốc nhiều = 3ThreadPoolExecutor is 3 times faster or 3x faster.

Đó là, phiên bản song song của chương trình với ThreadPoolExecutor nhanh hơn gấp 3 lần hoặc nhanh hơn gấp 3 lần.

  • Bạn có thể nhân số bội số tăng tốc 100 với tỷ lệ phần trăm

Tỷ lệ phần trăm tăng tốc = tăng tốc độ tăng tốc * 100

Trong ví dụ này, phiên bản song song nhanh hơn 300% so với phiên bản nối tiếp.

Làm thế nào để bạn đặt Chunksize trong bản đồ ()?map() function on the ThreadPoolExecutor takes a parameter called “chunksize” which defaults to 1.

Hàm bản đồ () trên ThreadPoolExecutor lấy một tham số có tên là Chunksize, mặc định là mặc định là 1.ThreadPoolExecutor; it is only used by the ProcessPoolExecutor, therefore you can safely ignore it.

Đặt tham số này không làm gì khi sử dụng ThreadPoolExecutor.ThreadPoolExecutor.

Làm thế nào để bạn gửi một nhiệm vụ tiếp theo?

Một số nhiệm vụ yêu cầu một nhiệm vụ thứ hai được thực hiện sử dụng kết quả từ nhiệm vụ đầu tiên theo một cách nào đó.

Chúng tôi có thể gọi đây là nhu cầu thực hiện một nhiệm vụ tiếp theo cho mỗi nhiệm vụ được gửi, có thể có điều kiện theo một cách nào đó.

Có một vài cách để gửi một nhiệm vụ tiếp theo.

Một cách tiếp cận sẽ là gửi nhiệm vụ tiếp theo vì chúng tôi đang xử lý kết quả từ nhiệm vụ đầu tiên.

Ví dụ: chúng ta có thể xử lý kết quả từ mỗi tác vụ đầu tiên khi chúng hoàn thành, sau đó gọi thủ công () cho mỗi nhiệm vụ tiếp theo khi cần và lưu trữ đối tượng mới trong tương lai trong danh sách thứ hai để sử dụng sau.

Chúng ta có thể làm ví dụ này về việc gửi các nhiệm vụ tiếp theo cụ thể với một ví dụ đầy đủ.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

# SuperfastPython.com

# Ví dụ về việc gửi các nhiệm vụ tiếp theo

từ thời gian nhập vào giấc ngủtime import sleep

từ nhập ngẫu nhiên ngẫu nhiênrandom import random

từ đồng thời.concurrent.futures import ThreadPoolExecutor

từ đồng thời.futures nhập as_completedconcurrent.futures import as_completed

# Kiểm tra giả hoạt động trong khoảnh khắc

DEF TASK1 ():task1():

    value=random()value=random()

    sleep(value)sleep(value)

& nbsp; & nbsp; & nbsp; & nbsp; in (f'task 1: {value} '))print(f'Task 1: {value}')

    returnvaluereturnvalue

# Kiểm tra giả hoạt động trong khoảnh khắc

DEF TASK1 ():task2(value1):

    value2=random()value2=random()

    sleep(value2)sleep(value2)

& nbsp; & nbsp; & nbsp; & nbsp; in (f'task 1: {value} '))print(f'Task 2: value1={value1}, value2={value2}')

    returnvalue2returnvalue2

DEF Task2 (value1):

& nbsp; & nbsp; & nbsp; & nbsp; print (f'task 2: value1 = {value1}, value2 = {value2} ')ThreadPoolExecutor(5)asexecutor:

# Bắt đầu nhóm chủ đề# send in the first tasks

    futures1=[executor.submit(task1)for_inrange(10)]futures1=[executor.submit(task1)for_inrange(10)]

với ThreadPoolExecutor (5) Asexecutor:# process results in the order they are completed

    futures2=list()futures2=list()

& nbsp; & nbsp; & nbsp; & nbsp;# gửi trong các nhiệm vụ đầu tiênforfuture1 inas_completed(futures1):

& nbsp; & nbsp; & nbsp; & nbsp;# kết quả quá trình theo thứ tự chúng được hoàn thành# get the result

        result=future1.result()result =future1.result()

& nbsp; & nbsp; & nbsp; & nbsp;# check if we should trigger a follow-up task

        ifresult>0.5:ifresult>0.5:

            future2=executor.submit(task2,result)future2 =executor.submit(task2,result)

            futures2.append(future2)futures2.append(future2)

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;# Nhận kết quả# wait for all follow-up tasks to complete

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp;#

& nbsp; & nbsp; & nbsp; & nbsp;# chờ cho tất cả các nhiệm vụ tiếp theo để hoàn thànhFuture object in a second list.

Chạy ví dụ bắt đầu một nhóm chủ đề với 5 luồng công nhân và gửi 10 nhiệm vụ.as_completed() function with a list of Future objects.

Sau đó chúng tôi xử lý kết quả cho các nhiệm vụ khi chúng được hoàn thành. Nếu kết quả từ vòng đầu tiên của các tác vụ yêu cầu một nhiệm vụ tiếp theo, chúng tôi gửi nhiệm vụ tiếp theo và theo dõi đối tượng trong tương lai trong danh sách thứ hai.

Các nhiệm vụ tiếp theo này được gửi khi cần thiết, thay vì chờ đợi cho đến khi tất cả các tác vụ vòng đầu tiên được hoàn thành, đây là một lợi ích tốt đẹp của việc sử dụng hàm AS_Completed () với danh sách các đối tượng trong tương lai.

Chúng ta có thể thấy rằng trong trường hợp này, năm nhiệm vụ vòng đầu tiên dẫn đến các nhiệm vụ tiếp theo.

Nhiệm vụ 1: 0,021868594663798424

Nhiệm vụ 1: 0,07220684891621587

Nhiệm vụ 1: 0.1889059597524675

Nhiệm vụ 1: 0.4044025009726221

Nhiệm vụ 1: 0,5377728619737125

Nhiệm vụ 1: 0,5627604576510364

Nhiệm vụ 1: 0.19590409149609522

Nhiệm vụ 1: 0.8350495785309672

Nhiệm vụ 2: Value1 = 0.8350495785309672, Value2 = 0.21472292885893007

Nhiệm vụ 2: Value1 = 0.5377728619737125, Value2 = 0.6180101068687799

Nhiệm vụ 1: 0.9916368220002719

Nhiệm vụ 1: 0.6688307514083958

Nhiệm vụ 2: Value1 = 0.6688307514083958, Value2 = 0.2691774622597396

Nhiệm vụ 2: Value1 = 0.5627604576510364, Value2 = 0.859736361909423

Nhiệm vụ 2: Value1 = 0.9916368220002719, Value2 = 0.6420604763057

Bạn có thể muốn sử dụng một nhóm luồng riêng cho các nhiệm vụ tiếp theo, để giữ mọi thứ riêng biệt.

Tôi không khuyên bạn nên gửi các nhiệm vụ mới từ trong một nhiệm vụ.

Điều này sẽ yêu cầu truy cập vào nhóm luồng như là một biến toàn cầu hoặc bằng cách được chuyển vào và sẽ phá vỡ ý tưởng về các nhiệm vụ là các chức năng thuần túy mà don don có tác dụng phụ, một thực tiễn tốt khi sử dụng các nhóm luồng.ThreadPoolExecutor.

Làm thế nào để bạn lưu trữ trạng thái địa phương cho mỗi chủ đề?

Bạn có thể sử dụng các biến cục bộ cho các chủ đề công nhân trong ThreadPoolExecutor.

Một mẫu phổ biến sẽ là sử dụng chức năng khởi tạo tùy chỉnh cho mỗi luồng công nhân để thiết lập một biến vị trí cụ thể cho mỗi luồng công nhân.

Các biến cục bộ này sau đó có thể được sử dụng bởi mỗi luồng trong mỗi tác vụ, yêu cầu nhiệm vụ phải nhận thức được cơ chế cục bộ luồng.

# chức năng để khởi tạo luồng công nhân

DEF INSIVERIZER_Worker (cục bộ):initializer_worker(local):

& nbsp; & nbsp; & nbsp; & nbsp;# tạo ra một giá trị duy nhất cho luồng công nhân# generate a unique value for the worker thread

    local.key=random()local.key=random()

& nbsp; & nbsp; & nbsp; & nbsp;# lưu trữ khóa công nhân duy nhất trong một biến cục bộ# store the unique worker key in a thread local variable

& nbsp;print(f'Initializing worker thread {local.key}')

Sau đó, chúng ta có thể xác định chức năng tác vụ mục tiêu của mình để lấy cùng một bối cảnh cục bộ và truy cập biến cục bộ của luồng cho luồng công nhân và sử dụng nó.

# một nhiệm vụ giả ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây

nhiệm vụ def (địa phương):task(local):

& nbsp; & nbsp; & nbsp; & nbsp;# truy cập khóa duy nhất cho luồng công nhân# access the unique key for the worker thread

    mykey=local.keymykey=local.key

& nbsp; & nbsp; & nbsp; & nbsp;# sử dụng nó# make use of it

    sleep(mykey)sleep(mykey)

& nbsp; & nbsp; & nbsp; & nbsp; returnf'worker sử dụng {mykey} 'returnf'Worker using {mykey}'

Sau đó, chúng tôi có thể định cấu hình phiên bản ThreadPoolExecutor mới của chúng tôi để sử dụng trình khởi tạo với đối số cục bộ cần thiết.ThreadPoolExecutor instance to use the initializer with the required local argument.

.....

# Nhận bối cảnh địa phương

local=threading.local()=threading.local()

# Tạo một nhóm chủ đề

executor=ThreadPoolExecutor(max_workers=2,initializer=initializer_worker,initargs=(local,))=ThreadPoolExecutor(max_workers=2, initializer=initializer_worker,initargs=(local,))

Sau đó gửi các tác vụ vào nhóm chủ đề với cùng một bối cảnh cục bộ.

.....

# Nhận bối cảnh địa phương

futures=[executor.submit(task,local)for_inrange(10)]=[executor.submit(task,local)for_inrange(10)]

# Tạo một nhóm chủ đềThreadPoolExecutor with thread local storage is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

Sau đó gửi các tác vụ vào nhóm chủ đề với cùng một bối cảnh cục bộ.

# Công văn hỏi

Kết hợp điều này lại với nhau, ví dụ hoàn chỉnh về việc sử dụng ThreadPoolExecutor với lưu trữ cục bộ được liệt kê bên dưới.time import sleep

# SuperfastPython.comrandom import random

# Ví dụ về lưu trữ cục bộ chủ đề cho chủ đề công nhânthreading

từ thời gian nhập vào giấc ngủconcurrent.futures import ThreadPoolExecutor

từ nhập ngẫu nhiên ngẫu nhiênconcurrent.futures import wait

# chức năng để khởi tạo luồng công nhân

DEF INSIVERIZER_Worker (cục bộ):initializer_worker(local):

& nbsp; & nbsp; & nbsp; & nbsp;# tạo ra một giá trị duy nhất cho luồng công nhân# generate a unique value for the worker thread

    local.key=random()local.key =random()

& nbsp; & nbsp; & nbsp; & nbsp;# lưu trữ khóa công nhân duy nhất trong một biến cục bộ# store the unique worker key in a thread local variable

& nbsp;print(f'Initializing worker thread {local.key}')

# một nhiệm vụ giả ngủ trong một khoảng thời gian ngẫu nhiên ít hơn một giây

nhiệm vụ def (địa phương):task(local):

& nbsp; & nbsp; & nbsp; & nbsp;# truy cập khóa duy nhất cho luồng công nhân# access the unique key for the worker thread

    mykey=local.keymykey=local.key

& nbsp; & nbsp; & nbsp; & nbsp;# sử dụng nó# make use of it

    sleep(mykey)sleep(mykey)

& nbsp; & nbsp; & nbsp; & nbsp; returnf'worker sử dụng {mykey} 'returnf'Worker using {mykey}'

# Nhận bối cảnh địa phương

local=threading.local()=threading.local()

# Tạo một nhóm chủ đề

executor=ThreadPoolExecutor(max_workers=2,initializer=initializer_worker,initargs=(local,))= ThreadPoolExecutor(max_workers=2,initializer=initializer_worker,initargs=(local,))

# Nhận bối cảnh địa phương

futures=[executor.submit(task,local)for_inrange(10)]= [executor.submit(task,local)for_inrange(10)]

# Tạo một nhóm chủ đề

Sau đó gửi các tác vụ vào nhóm chủ đề với cùng một bối cảnh cục bộ.future in futures:

    result=future.result()result=future.result()

    print(result)print(result)

# Công văn hỏi

executor.shutdown().shutdown()

print('done')('done')

Kết hợp điều này lại với nhau, ví dụ hoàn chỉnh về việc sử dụng ThreadPoolExecutor với lưu trữ cục bộ được liệt kê bên dưới.

# SuperfastPython.com

# Ví dụ về lưu trữ cục bộ chủ đề cho chủ đề công nhân

từ thời gian nhập vào giấc ngủ

từ nhập ngẫu nhiên ngẫu nhiên

nhập luồng

nhập luồng

từ nhập ngẫu nhiên ngẫu nhiên

nhập luồng

từ nhập ngẫu nhiên ngẫu nhiên

nhập luồng

từ nhập ngẫu nhiên ngẫu nhiên

nhập luồng

từ nhập ngẫu nhiên ngẫu nhiên

nhập luồng

từ đồng thời.

từ đồng thời.ThreadPoolExecutor.

# Đợi tất cả các luồng hoàn thànhadd_done_callback() function.

Infutures forfuture:

# Tắt máy nhóm chủ đề

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

Sau đó gửi các tác vụ vào nhóm chủ đề với cùng một bối cảnh cục bộ.

# Công văn hỏi

Kết hợp điều này lại với nhau, ví dụ hoàn chỉnh về việc sử dụng ThreadPoolExecutor với lưu trữ cục bộ được liệt kê bên dưới.time import sleep

# SuperfastPython.comrandom import random

từ thời gian nhập vào giấc ngủconcurrent.futures import ThreadPoolExecutor

từ nhập ngẫu nhiên ngẫu nhiênconcurrent.futures import wait

nhập luồng

từ đồng thời.progress_indicator(future):

    print('.',end='',flush=True)print('.',end='',flush=True)

từ đồng thời.

# Đợi tất cả các luồng hoàn thànhtask(name):

    sleep(random())sleep(random())

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (2) Asexecutor:ThreadPoolExecutor(2)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# gửi trong các nhiệm vụ# send in the tasks

    futures=[executor.submit(task,i)foriinrange(20)]futures=[executor.submit(task,i)foriinrange(20)]

& nbsp; & nbsp; & nbsp; & nbsp;# đăng ký gọi lại chỉ báo tiến trình# register the progress indicator callback

& nbsp; & nbsp; & nbsp; & nbsp; forfuture Infutures:forfuture infutures:

        future.add_done_callback(progress_indicator)future.add_done_callback(progress_indicator)

& nbsp; & nbsp; & nbsp; & nbsp;# chờ cho tất cả các nhiệm vụ hoàn thành# wait for all tasks to complete

in ('\ ndone!')('\nDone!')

Chạy ví dụ bắt đầu một nhóm chủ đề với hai luồng công nhân và gửi 20 nhiệm vụ.

Chức năng gọi lại chỉ báo tiến trình được đăng ký với mỗi đối tượng trong tương lai in một dấu chấm khi mỗi tác vụ được hoàn thành, đảm bảo rằng đầu ra tiêu chuẩn được xóa với mỗi cuộc gọi để in () và không có dòng mới nào được in.Future object that prints one dot as each task is completed, ensuring that the standard output is flushed with each call to print() and that no new line is printed.

Điều này đảm bảo rằng mỗi chúng ta thấy dấu chấm ngay lập tức bất kể luồng in và tất cả các dấu chấm xuất hiện trên một dòng.

Mỗi nhiệm vụ sẽ hoạt động trong một khoảng thời gian thay đổi ít hơn một giây và một dấu chấm được in sau khi nhiệm vụ được hoàn thành.

....................

Done!

Một chỉ số tiến độ phức tạp hơn phải biết tổng số tác vụ và sẽ sử dụng bộ đếm an toàn chủ đề để cập nhật trạng thái của số lượng nhiệm vụ được hoàn thành trong tất cả các nhiệm vụ sẽ được hoàn thành.

Chúng ta có cần phải kiểm tra __main__ không?

Bạn không cần phải kiểm tra __main__ khi sử dụng ThreadPoolExecutor.__main__ when using the ThreadPoolExecutor.

Bạn cần kiểm tra __main__ khi sử dụng phiên bản quy trình của nhóm, được gọi là ProcessPoolExecutor. Đây có thể là nguồn gốc của sự nhầm lẫn.__main__ when using the Process version of the pool, called a ProcessPoolExecutor. This may be the source of confusion.

Làm thế nào để bạn có được một đối tượng trong tương lai cho các tác vụ được thêm vào bản đồ ()?

Khi bạn gọi bản đồ (), nó sẽ tạo ra một đối tượng trong tương lai cho mỗi nhiệm vụ.map(), it does create a Future object for each task.

Trong nội bộ, gửi () được gọi cho mỗi mục trong số lượng được cung cấp cho cuộc gọi đến MAP ().submit() is called for each item in the iterable provided to the call to map().

Tuy nhiên, không có cách nào rõ ràng để truy cập đối tượng trong tương lai cho các tác vụ được gửi vào nhóm luồng thông qua bản đồ ().Future object for tasks sent into the thread pool via map().

Mỗi tác vụ trên hàng đợi công việc nội bộ của rentpoolExecutor đối tượng là một ví dụ của _workItem có tham chiếu đến đối tượng trong tương lai cho nhiệm vụ. Bạn có thể truy cập vào hàng đợi nội bộ của đối tượng ThreadPoolExecutor, nhưng bạn không có cách nào an toàn để liệt kê các mục trong hàng đợi mà không xóa chúng.ThreadPoolExecutor object’s internal work queue is an instance of a _WorkItem that does have a reference to the Future object for the task. You can access the ThreadPoolExecutor object’s internal queue, but you have no safe way of enumerating items in the queue without removing them.

Nếu bạn cần một đối tượng trong tương lai cho một nhiệm vụ, hãy gọi hãy gửi ().Future object for a task, call submit().

Tôi có thể gọi Shutdown () từ trong Trình quản lý ngữ cảnh không?

Bạn có thể gọi Shutdown () trong Trình quản lý ngữ cảnh, nhưng không có nhiều trường hợp sử dụng.shutdown() within the context manager, but there are not many use cases.

Nó không gây ra lỗi mà tôi có thể thấy.

Bạn có thể muốn gọi Shutdown () rõ ràng nếu bạn muốn hủy tất cả các tác vụ theo lịch trình và bạn không có quyền truy cập vào các đối tượng trong tương lai và bạn muốn làm sạch khác trước khi chờ tất cả các tác vụ chạy dừng lại.shutdown() explicitly if you wish to cancel all scheduled tasks and you don’t have access to the Future objects, and you wish to do other clean-up before waiting for all running tasks to stop.

Sẽ thật kỳ lạ nếu bạn thấy mình trong tình huống này.

Tuy nhiên, đây là một ví dụ về việc gọi tắt () từ trong Trình quản lý ngữ cảnh.shutdown() from within the context manager.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# SuperfastPython.com

# Ví dụ về việc tắt trong Trình quản lý ngữ cảnh

từ thời gian nhập vào giấc ngủtime import sleep

từ đồng thời.concurrent.futures import ThreadPoolExecutor

# Kiểm tra giả hoạt động trong khoảnh khắc

nhiệm vụ def (tên):task(name):

    sleep(2)sleep(2)

& nbsp; & nbsp; & nbsp; & nbsp; print (f'done: {name} ')print(f'Done: {name}')

# Bắt đầu nhóm chủ đề

với ThreadPoolExecutor (1) Asexecutor:ThreadPoolExecutor(1)asexecutor:

& nbsp; & nbsp; & nbsp; & nbsp;# gửi một số nhiệm vụ vào nhóm chủ đề# send some tasks into the thread pool

& nbsp; & nbsp; & nbsp; & nbsp; in ('gửi trong các nhiệm vụ ...')print('Sending in tasks...')

    futures=[executor.submit(task,i)foriinrange(10)]futures=[executor.submit(task,i)foriinrange(10)]

& nbsp; & nbsp; & nbsp; & nbsp;# tắt rõ ràng trong trình quản lý bối cảnh# explicitly shutdown within the context manager

& nbsp;print('Shutting down...')

    executor.shutdown(wait=False,cancel_futures=True)executor.shutdown(wait=False,cancel_futures=True)

& nbsp; & nbsp; & nbsp; & nbsp;# shutdown đã gọi lại ở đây khi Trình quản lý bối cảnh thoát ra# shutdown called again here when context manager exited

    print('Waiting...')print('Waiting...')

In ('Làm những việc khác ...')('Doing other things...')

Chạy ví dụ bắt đầu một nhóm luồng với một luồng công nhân và sau đó gửi trong mười tác vụ để thực thi.

Sau đó, chúng tôi gọi tắt rõ ràng việc tắt máy vào nhóm luồng và hủy tất cả các nhiệm vụ theo lịch trình mà không cần chờ đợi.

Sau đó chúng tôi thoát khỏi Trình quản lý bối cảnh và chờ tất cả các tác vụ hoàn thành. Việc tắt máy thứ hai này hoạt động như mong đợi, chờ đợi một nhiệm vụ chạy hoàn thành trước khi quay lại.

Gửi nhiệm vụ ...

Đang Tắt...

Waiting...

Xong: 0

Làm những việc khác ...

Phản đối phổ biến khi sử dụng ThreadPoolExecutor

ThreadPoolExecutor có thể không phải là giải pháp tốt nhất cho tất cả các vấn đề đa đọc trong chương trình của bạn.ThreadPoolExecutor may not be the best solution for all multithreading problems in your program.

Điều đó đang được nói, cũng có thể có một số hiểu lầm đang ngăn bạn sử dụng đầy đủ và tốt nhất các khả năng của ThreadPoolExecutor trong chương trình của bạn.ThreadPoolExecutor in your program.

Trong phần này, chúng tôi xem xét một số phản đối phổ biến được các nhà phát triển thấy khi xem xét sử dụng ThreadPoolExecutor trong mã của họ.ThreadPoolExecutor in their code.

Điều gì về khóa phiên dịch toàn cầu (GIL)?

Khóa thông dịch viên toàn cầu, hoặc ngắn hoàn toàn là Gil, là một quyết định thiết kế với trình thông dịch Python tham chiếu.

Nó đề cập đến thực tế là việc triển khai trình thông dịch Python sử dụng khóa chính để ngăn chặn nhiều hơn một hướng dẫn Python thực thi cùng một lúc.

Điều này ngăn chặn nhiều hơn một luồng thực thi trong các chương trình Python, cụ thể trong mỗi quy trình Python, đó là mỗi trường hợp của trình thông dịch Python.

Việc thực hiện GIL có nghĩa là các luồng python có thể đồng thời, nhưng không thể chạy song song. Hãy nhớ lại rằng đồng thời có nghĩa là có nhiều hơn một nhiệm vụ có thể được tiến hành cùng một lúc; song song có nghĩa là nhiều hơn một nhiệm vụ thực sự thực hiện cùng một lúc. Các nhiệm vụ song song là đồng thời, các nhiệm vụ đồng thời có thể hoặc không thể thực hiện song song.

Đó là lý do đằng sau heuristic mà các chủ đề Python chỉ nên được sử dụng cho các nhiệm vụ ràng buộc IO, chứ không phải các nhiệm vụ liên kết CPU, vì các nhiệm vụ ràng buộc IO sẽ chờ trong hạt nhân hệ điều hành để các tài nguyên từ xa phản hồi (không thực hiện các hướng dẫn Python) , cho phép các chủ đề Python khác chạy và thực hiện các hướng dẫn Python.

Nói cách khác, GIL không có nghĩa là chúng ta không thể sử dụng các chủ đề trong Python, chỉ có một số trường hợp sử dụng cho các chủ đề Python là khả thi hoặc phù hợp.

Quyết định thiết kế này được đưa ra trong quá trình thực hiện tham chiếu của phiên dịch Python (từ Python.org), nhưng có thể không tác động đến các phiên dịch viên khác (như Pypy, Iron Python và Jython) cho phép nhiều hướng dẫn Python được thực hiện đồng thời và song song.

Bạn có thể tìm hiểu thêm về chủ đề này ở đây:

  • ThreadPoolExecutor so với khóa thông dịch toàn cầu (GIL)

Có phải chủ đề Python có phải là chủ đề thực tế không?

Yes.

Python sử dụng các chủ đề cấp hệ thống thực, còn được gọi là chủ đề cấp kernel, khả năng được cung cấp bởi các hệ điều hành hiện đại như Windows, Linux và MacOS.

Chủ đề Python không phải là chủ đề cấp phần mềm, đôi khi được gọi là chủ đề cấp độ người dùng hoặc luồng màu xanh lá cây.

Aren sắt python chủ đề lỗi?

No.

Chủ đề Python không phải là lỗi.

Python Threading là khả năng hạng nhất của nền tảng Python và đã được một thời gian rất dài.

Có phải là Python Python là một lựa chọn tồi cho sự đồng thời?

Các nhà phát triển yêu thích Python vì nhiều lý do, phổ biến nhất là vì nó dễ sử dụng và nhanh chóng để phát triển.

Python thường được sử dụng cho mã keo, tập lệnh một lần, nhưng ngày càng nhiều cho các hệ thống phần mềm quy mô lớn.

Nếu bạn đang sử dụng Python và sau đó bạn cần đồng thời, thì bạn làm việc với những gì bạn có. Câu hỏi là moot.

Nếu bạn cần đồng thời và bạn chưa chọn ngôn ngữ, có lẽ một ngôn ngữ khác sẽ phù hợp hơn, hoặc có lẽ không. Xem xét phạm vi đầy đủ của các yêu cầu chức năng và phi chức năng (hoặc nhu cầu, mong muốn và mong muốn của người dùng) cho dự án của bạn và khả năng của các nền tảng phát triển khác nhau.

Tại sao không luôn luôn sử dụng ProcessPoolExecutor thay thế?

ProcessPoolExecutor hỗ trợ nhóm các quy trình, không giống như ThreadPoolExecutor hỗ trợ nhóm các luồng.ProcessPoolExecutor supports pools of processes, unlike the ThreadPoolExecutor that supports pools of threads.

Chủ đề và quy trình khá khác nhau và chọn cái này là cố ý.

Một chương trình Python là một quá trình có chủ đề chính. Bạn có thể tạo nhiều luồng bổ sung trong một quy trình Python. Bạn cũng có thể nĩa hoặc sinh ra nhiều quy trình Python, mỗi quy trình sẽ có một luồng và có thể sinh ra các luồng bổ sung.

Nhìn rộng hơn, các luồng nhẹ và có thể chia sẻ bộ nhớ (dữ liệu và biến) trong một quy trình, trong khi các quá trình là nặng và yêu cầu chi phí cao hơn và áp đặt nhiều giới hạn hơn trong việc chia sẻ bộ nhớ (dữ liệu và biến).

Thông thường, các quy trình được sử dụng cho các nhiệm vụ và luồng liên kết CPU được sử dụng cho các nhiệm vụ gắn IO, và đây là một heuristic tốt, nhưng điều này không phải là trường hợp.

Có lẽ ProcessPoolExecutor là phù hợp hơn cho vấn đề cụ thể của bạn. Có lẽ hãy thử nó và xem.ProcessPoolExecutor is a better fit for your specific problem. Perhaps try it and see.

Bạn có thể tìm hiểu thêm về chủ đề này ở đây:

  • ThreadPoolExecutor so với khóa thông dịch toàn cầu (GIL)

Có phải chủ đề Python có phải là chủ đề thực tế không?

Python sử dụng các chủ đề cấp hệ thống thực, còn được gọi là chủ đề cấp kernel, khả năng được cung cấp bởi các hệ điều hành hiện đại như Windows, Linux và MacOS.ThreadPoolExecutor is like the “auto mode” for Python threading.

Chủ đề Python không phải là chủ đề cấp phần mềm, đôi khi được gọi là chủ đề cấp độ người dùng hoặc luồng màu xanh lá cây.threading.Thread class directly.

Aren sắt python chủ đề lỗi?

Chủ đề Python không phải là lỗi.

Python Threading là khả năng hạng nhất của nền tảng Python và đã được một thời gian rất dài.Thread class with the target keyword for pure functions (functions that don’t have side effects), perhaps you would be better suited to using the ThreadPoolExecutor.

Bạn có thể tìm hiểu thêm về chủ đề này ở đây:

  • ThreadPoolExecutor so với Thread trong Python

Tại sao không sử dụng asyncio?

Asyncio có thể là một giải pháp thay thế cho việc sử dụng ThreadPoolExecutor.ThreadPoolExecutor.

Asyncio được thiết kế để hỗ trợ số lượng lớn các hoạt động IO, có lẽ hàng chục ngàn đến hàng chục ngàn, tất cả trong một luồng.

Nó đòi hỏi một mô hình lập trình thay thế, được gọi là lập trình phản ứng, có thể là thách thức cho người mới bắt đầu.

Tuy nhiên, nó có thể là một sự thay thế tốt hơn để sử dụng một nhóm chủ đề cho nhiều ứng dụng.

Bạn có thể tìm hiểu thêm về chủ đề này ở đây:

  • ThreadPoolExecutor so với Asyncio trong Python

Đọc thêm

Phần này liệt kê các tài nguyên bổ sung hữu ích về chủ đề này.

Sách

  • Tờ Cheat API của ThreadPoolExecutor
  • Câu hỏi phỏng vấn API tương lai đồng thời
  • ThreadPoolExecutor Jump bắt đầu (khóa học 7 ngày của tôi)

API

  • Chức năng tích hợp Python
  • Đồng thời.
  • cpython/lib/đồng thời/tương lai/mã nguồn
  • cpython/lib/đồng thời/tương lai/_base.py mã nguồn
  • Chủ đề-song song dựa trên luồng
  • Đa xử lý-song song dựa trên quy trình
  • Hàng đợi - Một lớp xếp hàng đồng bộ
  • Ghi nhật ký - Cơ sở đăng nhập cho Python

Người giới thiệu

  • Chủ đề (Điện toán), Wikipedia.
  • Trang chủ Brian Quinlan.
  • Tương lai và lời hứa, Wikipedia.
  • Chức năng thuần túy, Wikipedia.

Kết luận

Đây là một hướng dẫn lớn và bạn đã phát hiện ra rất chi tiết về cách thức hoạt động của ThreadPoolExecutor và cách sử dụng tốt nhất trong dự án của bạn.ThreadPoolExecutor works and how to best use it on your project.

Bạn có thấy hướng dẫn này hữu ích không? Tôi rất thích biết, xin vui lòng chia sẻ một từ tử tế trong các bình luận dưới đây.
I’d love to know, please share a kind word in the comments below.

Bạn đã sử dụng ThreadPoolExecutor trong một dự án chưa? Tôi rất thích nghe về nó, xin vui lòng cho tôi biết trong các bình luận.
I’d love to hear about it, please let me know in the comments.

Bạn có câu hỏi nào không? Để lại câu hỏi của bạn trong một bình luận bên dưới và tôi sẽ trả lời nhanh với lời khuyên tốt nhất của tôi.
Leave your question in a comment below and I will reply fast with my best advice.