How does ThreadPoolExecutor().map differ from ThreadPoolExecutor().submit?

后端 未结 4 1239
耶瑟儿~
耶瑟儿~ 2021-01-30 06:45

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:
             


        
4条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2021-01-30 07:03

    In addition to the explanation in the answers here, it can be helpful to go right to the source. It reaffirms the statement from another answer here that:

    • .map() gives results in the order they are submitted, while
    • iterating over a list of Future objects with concurrent.futures.as_completed() won't guarantee this ordering, because this is the nature of as_completed()

    .map() is defined in the base class, concurrent.futures._base.Executor:

    class Executor(object):
        def submit(self, fn, *args, **kwargs):
            raise NotImplementedError()
    
        def map(self, fn, *iterables, timeout=None, chunksize=1):
            if timeout is not None:
                end_time = timeout + time.monotonic()
    
            fs = [self.submit(fn, *args) for args in zip(*iterables)]  # 

    As you mention, there is also .submit(), which left to be defined in the child classes, namely ProcessPoolExecutor and ThreadPoolExecutor, and returns a _base.Future instance that you need to call .result() on to actually make do anything.

    The important lines from .map() boil down to:

    fs = [self.submit(fn, *args) for args in zip(*iterables)]
    fs.reverse()
    while fs:
        yield fs.pop().result()
    

    The .reverse() plus .pop() is a means to get the first-submitted result (from iterables) to be yielded first, the second-submitted result to be yielded second, and so on. The elements of the resulting iterator are not Futures; they're the actual results themselves.

提交回复
热议问题