Waitgroups in Python

To wait for multiple threads to finish, we can use a ThreadPoolExecutor with a Future list in Python.

import time
from concurrent.futures import ThreadPoolExecutor, as_completed

# This is the function we'll run in every thread.
def worker(id):
    print(f"Worker {id} starting")
    
    # Sleep to simulate an expensive task.
    time.sleep(1)
    print(f"Worker {id} done")

def main():
    # This ThreadPoolExecutor is used to manage the thread pool
    with ThreadPoolExecutor(max_workers=5) as executor:
        # Launch several threads and store their futures
        futures = []
        for i in range(1, 6):
            # Submit the worker function to the executor
            future = executor.submit(worker, i)
            futures.append(future)
        
        # Wait for all futures to complete
        for future in as_completed(futures):
            future.result()

    # Note that this approach allows for straightforward error propagation.
    # If a worker raises an exception, it will be re-raised here when calling future.result()

if __name__ == "__main__":
    main()

To run the program:

$ python threadpool.py
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done

The order of workers starting up and finishing is likely to be different for each invocation.

In this Python version, we use the concurrent.futures module, which provides a high-level interface for asynchronously executing callables. The ThreadPoolExecutor manages a pool of worker threads, similar to how Go manages goroutines.

Instead of a WaitGroup, we use the Future objects returned by executor.submit(). These Future objects represent the asynchronous execution of our worker function. We collect all these futures and then use as_completed() to iterate over them as they complete.

This approach provides a way to wait for all threads to complete, similar to the WaitGroup.Wait() in the original example. It also allows for easy error propagation: if a worker raises an exception, it will be re-raised when we call future.result().

The with statement ensures that the ThreadPoolExecutor is properly shut down after all tasks are complete, similar to how Go manages the lifecycle of goroutines.

查看推荐产品