as_completed和wait源码分析

本文详细分析了Python中多线程处理中`as_completed`和`wait`函数的源码,解释了它们在处理Future对象时的作用。`as_completed`返回一个生成器,按完成顺序产出Future,有去重功能;`wait`函数则根据`return_when`参数决定何时返回,可设置在任何完成、首次异常或全部完成时返回。文章通过示例和源码解读阐述了这两个函数的工作原理。
摘要由CSDN通过智能技术生成

as_completed和wait源码分析


前言

在ThreadPoolExecutor引导的多线程开发中,有as_completed()wait()两个辅助函数。下面结合源码分析它们各自作用。因后面多次提到事件锁,也许,你需要对它事先了解Python同步机制(semaphore,event,queue)

(以下基于Python3.7)

as_complete

def greet():
    print("hello world")

if __name__ == "__main__":
    executor = ThreadPoolExecutor()

    task = executor.submit(greet)
    print(type(task))  # 输出:<class 'concurrent.futures._base.Future'>

    results = as_completed([task])
    print(type(results))  # 输出:<class 'generator'>

    for result in results:
        print(type(result))  # 输出:<class 'concurrent.futures._base.Future'>

最初知道有as_completed()这个函数我是很不解的,因为如上面代码所示,submit()提交任务后拿到的返回值是Future对象,经过as_completed包装后成了生成器,但是打开生成器一看,结果还是Future对象!这破玩意就这点用处?显示不是。官方文档对其做的说明,揭露了它的本质:

Returns an iterator over the Future instances (possibly created by different Executor instances) given by fs that yields futures as they complete (finished or were cancelled). Any futures given by fs that are duplicated will be returned once. Any futures that completed before as_completed() is called will be yielded first. The returned iterator raises a concurrent.futures.TimeoutError if _next_() is called and the result isn’t available after timeout seconds from the original call to as_completed(). timeout can be an int or float. If timeout is not specified or None, there is no limit to the wait time.

在这里边有两句话比较重要:

  • Any futures given by fs that are duplicated will be returned once.
  • Any futures that completed before as_completed() is called will be yielded first.

先看第一句:当future重复时,只返回一次。示例如下:

def greet(word):
    return word

if __name__ == "__main__"
    executor = ThreadPoolExecutor()

    tasks = [executor.submit(greet, word) for word in ["hello", "world"]]
    tasksDouble = tasks * 2  # futures x 2
    for item in as_completed(tasksDouble):
        print(item.result())

# 输出:
hello
world

可以看出,我们对tasks做了乘2操作,但是经手as_completed()之后并没有重复打印hello或者word。说明在as_completed中有去重操作。Python内部仅仅做了一个很简单的处理——集合真是强大的去重助理。

# as_completed源码
def as_completed(fs, timeout=None):
    ...
    fs = set(fs)  # 去重操作
    ...

再看第二句:as_completed会先把该函数调用之前完成的furture依次yield出去。也就是说,返回结果不会顺序了。似乎莫名奇妙,但我们来看看用as_completed和不用的区别。

# 不使用as_completed
def print_num(order):
	"""
	i 表示线程启动次序
	通过随机获取num, 使得线程与线程之间的结束时间可能不同
	"""
    num = random.randrange(10)
    time.sleep(num)

    ordict = collections.OrderedDict()
    ordict["oroder"] = order
    ordict["value"] = num
    return ordict  # 最后打印调用次序以及线程运行的近似时间

if __name__ == "__main__":
    executor = ThreadPoolExecutor()
    alltasks = [executor.submit(print_num, i) for i in range(10)]

    for task in alltasks
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`concurrent.futures.as_completed()`是一个非常有用的函数,它可以迭代返回已完成的`Future`对象。在使用线程池执行一批任务时,可以使用`as_completed()`来获取已完成的任务的结果。 下面是一个示例: ```python import concurrent.futures # 定义一个任务函数 def task(name): print(f"Task {name} starting") # 执行具体任务的代码 print(f"Task {name} done") return f"Task {name} result" # 创建线程池 with concurrent.futures.ThreadPoolExecutor() as executor: # 提交任务到线程池 futures = [executor.submit(task, i) for i in range(5)] # 获取已完成任务的结果 for future in concurrent.futures.as_completed(futures): result = future.result() print(result) ``` 在上面的示例中,我们首先定义了一个任务函数`task`,然后使用`ThreadPoolExecutor`创建了一个线程池,并使用`submit`方法提交了5个任务到线程池中。返回的`futures`是一个包含所有任务的`Future`对象列表。 接下来,在使用`as_completed()`函数时,它会返回一个迭代器,可以逐个获取已完成的任务。我们使用`future.result()`方法获取任务的结果,并打印出来。 需要注意的是,`as_completed()`函数返回的是一个迭代器,它会按照任务完成的顺序返回结果。如果一个任务比其他任务耗时更长,那么在迭代结果时,可能会先返回其他任务的结果。因此,你可以根据实际需求来处理返回的结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值