function of `with` in `concurrent.futures`

Using a concurrent.futures.Executor in a with statement is equivalent to calling Executor.shutdown after using it – causing the executor to wait for all tasks to complete. An Executor used in a with guarantees proper shutdown of concurrent tasks even if an error occurs inside the with block.

Executor.shutdown(wait=True)

Signal the executor that it should free any resources that it is using when the currently pending futures are done executing. Calls to Executor.submit() and Executor.map() made after shutdown will raise RuntimeError.

[...]

You can avoid having to call this method explicitly if you use the with statement, which will shutdown the Executor (waiting as if Executor.shutdown() were called with wait set to True): [...]


concurrent.futures is not well documented. When you create an executor it must be shutdown to terminate its threads or processes. That code will signal the threads to exit by pushing a None command to them and then wait for them to complete. In your first case, had you added executor.shutdown() you'd see the delay. It was there anyway - the program still took 10 seconds to exit.

The executor can be used as a context manager (it as __enter__ and __exit__ methods). When it exits the "with" block, __exit__ is called and it in turn does the shutdown call for you.