Skip to content
Search

Python:concurrent.futures

lastmod 2023-10-06created 2023-10-062 edits

ThreadPoolExecutor

ThreadPoolExecutor uses worker threads internally.

INFORMATION

This is mainly because the GIL (Global Interpreter Lock) prevents parallel execution of threads within a process, so it is suited to IO-bound tasks rather than CPU-bound tasks

ThreadPoolExecutor uses worker threads, which are a system-level construct. The operating system chooses which thread runs on a CPU core at a given time.

ThreadPoolExecutor vs asyncio

ThreadPoolExecutor achieves concurrency through multitasking, whereas AsyncIO achieves concurrency through cooperative multitasking.

When to use ThreadPoolExecutor

  • Uses threads, not coroutines.
  • System level, not software level.
  • Preemptive multitasking, not cooperative.
  • Tasks are any function, not restricted.
  • Thread safety is required.
  • Create 100 threads, not 1000.

Let's look at some common cases where ThreadPoolExecutor is a good fit. Use ThreadPoolExecutor when:

  • Your tasks can be defined as pure functions with no state or side effects.
  • Your tasks fit within a single Python function, making them simple and easy to understand.
  • You need to perform the same task (e.g. homogeneous tasks) many times.
  • You need to apply the same function to each object in a collection in a for loop.

The best thing about ThreadPoolExecutor is converting Python programs with relatively independent IO-bound tasks to run concurrently.

You can build a program around a thread pool, but it is more likely that you will conceive, implement, and test the program with sequential code, then use ThreadPoolExecutor to run it concurrently and improve the program's performance.

When to use AsyncIO

  • Uses coroutines, not threads.
  • Software level, not system level.
  • Cooperative multitasking, not preemptive.
  • Tasks are awaitables, not any function.
  • No thread safety issues.
  • Create 100,000+ coroutines, reasonably unbounded.

Let's look at some common cases where AsyncIO is a good fit. Use AsyncIO when:

  • You want to or need to explicitly adopt the asynchronous programming paradigm.
  • You need to run 1000+ concurrent IO-bound tasks.

AsyncIO has been proposed as an alternative to Python threads, but this is misleading.

You must embrace the asynchronous programming paradigm from the start; it cannot be added later the way ThreadPoolExecutor can. In this way it can be similar to extending the Thread class, in that it must be considered up front while designing the program. AsyncIO is best for programs that perform many IO-bound tasks, like ThreadPoolExecutor. Unlike ThreadPoolExecutor, the IO-bound tasks must also have a non-blocking API, such as the asyncio module or a third-party library. Fully embracing asynchronous programming and the asyncio module has the benefit of producing more capable code.

For example, with coroutines you can easily dispatch tens or hundreds of thousands of IO-bound tasks without waiting on the operating system to instantiate a system thread for each task, as you would with a thread pool. This makes asynchronous programming in general possible, and the asyncio module is especially suited to programs that need a vast number of IO-bound tasks, such as:

  • A client program connected to many servers.
  • A server program that supports connections from many clients.
  • A P2P program that supports connections with many peers.

See also

References


  1. ThreadPoolExecutor_vs_AsyncIO_in_Python.pdf ↩

author Zer0 sitemap