I was just very confused by some code that I wrote. I was surprised to discover that:
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(f, iterable))
and
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
results = list(map(lambda x: executor.submit(f, x), iterable))
produce different results. The first one produces a list of whatever type f
returns, the second produces a list of concurrent.futures.Future
objects that then need to be evaluated with their result()
method in order to get the value that f
returned.
My main concern is that this means that executor.map
can't take advantage of concurrent.futures.as_completed
, which seems like an extremely convenient way to evaluate the results of some long-running calls to a database that I'm making as they become available.
I'm not at all clear on how concurrent.futures.ThreadPoolExecutor
objects work -- naively, I would prefer the (somewhat more verbose):
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
result_futures = list(map(lambda x: executor.submit(f, x), iterable))
results = [f.result() for f in futures.as_completed(result_futures)]
over the more concise executor.map
in order to take advantage of a possible gain in performance. Am I wrong to do so?