Python 使用concurrent.futures每秒并行调用一个fn
我一直在努力掌握如何使用concurrent.futures每秒调用函数3次,而不必等待函数返回。我会在打完所有需要打的电话后收集结果 这就是我现在所处的位置,我很惊讶这个示例函数中的sleep()阻止了我的代码启动下一块3个函数调用。显然,我对这里的文档理解不够:) 这段代码导致打印3个块,每个块之间的睡眠时间为10秒。我如何更改这段代码以确保在调用下一批之前不等待函数返回Python 使用concurrent.futures每秒并行调用一个fn,python,concurrent.futures,Python,Concurrent.futures,我一直在努力掌握如何使用concurrent.futures每秒调用函数3次,而不必等待函数返回。我会在打完所有需要打的电话后收集结果 这就是我现在所处的位置,我很惊讶这个示例函数中的sleep()阻止了我的代码启动下一块3个函数调用。显然,我对这里的文档理解不够:) 这段代码导致打印3个块,每个块之间的睡眠时间为10秒。我如何更改这段代码以确保在调用下一批之前不等待函数返回 感谢首先,对于当前区块(字母表,3):的每次迭代,您将创建一个新的ProcessPoolExecutor实例和futur
感谢首先,对于当前区块(字母表,3):的每次迭代,您将创建一个新的
ProcessPoolExecutor
实例和futures
字典实例,以取代上一个实例。因此,as_completed(futures)中result的最后一个循环:
将只打印提交的最后一个区块的结果。其次,也是我认为您挂起的原因,您的块由管理,ProcessPoolExecutor(max_workers=3)作为executor:
将不会终止,直到executor
提交的任务完成,并且至少需要10秒。因此,当前块的下一次迭代(字母表,3):
块的执行频率不会超过每10秒一次
还请注意,由于相同的原因,导致as_completed(futures):的块需要在中移动,ThreadPoolExecutor(max_workers=26)作为执行器:
块。也就是说,如果它放在后面,则在所有任务完成之前不会执行它,因此您将无法“在任务完成时”获得结果
您需要按如下所示进行一些重新安排(我还修改了print\u something
,以返回除None
以外的内容。如果您有足够的工作人员(26人)来运行提交的26项任务,那么现在应该不会出现挂起。我怀疑您的桌面(如果您在PC上运行此功能)有26个内核来支持26个并发执行的进程。但我注意到,print_something
只打印一个短字符串,然后休眠10秒,这允许它将其处理器交给池中的另一个进程。因此,对于cpu密集型任务,通过指定max_workers
值g几乎没有什么好处在本例中,它比您计算机上实际的物理处理器/内核的数量还要多,但当您的任务花费很少的时间执行实际的Python字节码时,更有效的方法是使用线程而不是进程,因为创建线程的成本远低于创建进程的成本。但是,线程当您运行的任务主要由Python字节代码组成时,它的性能非常差,因为由于全局解释器锁(GIL)的序列化,这些代码无法并发执行
供您研究的主题:全局解释器锁(GIL)和Python字节码执行
更新以使用线程:
因此,我们应该用26个或更多的轻量级线程来代替ProcessPoolExecutor
。concurrent.futures
模块的优点是不需要更改其他代码。但最重要的是更改块结构,并使用单个执行器
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
def print_something(thing):
# NOT cpu-intensive, so threads should work well here
print(thing)
time.sleep(10)
return thing # so there is a non-None result
# define a generator
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
def main():
chunk_number = 0
alphabet = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
futures = {}
with ThreadPoolExecutor(max_workers=26) as executor:
for current_chunk in chunks(alphabet, 3): # Restrict to calling the function 3 times per second
futures.update({executor.submit(print_something, thing): thing for thing in current_chunk })
chunk_number += 1
print('chunk %s' % chunk_number)
time.sleep(1)
# needs to be within the executor block else it won't run until all futures are complete
for result in as_completed(futures):
print(result.result())
if __name__ == '__main__':
main()
你能提供一些输出吗?@Algebra8当然-我得到chunk1abc
,然后是10秒的延迟,然后chunk2def
等等。你希望得到它们,而不必排队等待结果的完成
?我不在乎什么时候收到结果-我在乎的是确保每一秒都有功能on被调用3次。print_something()中的sleep()调用只是为了看看如果函数需要很长时间才能响应会发生什么(模拟缓慢的网络连接等)。我不希望函数的执行时间减慢调用它的速度。谢谢。结果仍然不是我所期望的-我现在得到了以下结果chunk 1 a b c chunk 2 chunk 3 chunk 4 chunk 5 chunk 6 chunk 7 chunk 8 chunk 9 p d e f g h
,结果之间存在延迟。有什么想法吗?你真的不清楚你想要完成什么。现在你总共有3个处理器在处理27个“任务”。必须有延迟。在更改之前,您创建了多个池,每个池包含3个进程,或者至少尝试创建。但我的桌面PC只有8个内核,因此我将获得的最大多处理级别为8,因此最终需要等待。通过将ProcessPoolExecutor
替换为ThreadPoo,将代码更改为使用线程lExecutor
并指定max_workers=27
或更多。然后让它运行。您可以使用线程,因为您的任务是I/O绑定的(它们大多等待)。通过最新的更改,我现在得到了我所期望的结果。对于您的解决方案的第一个版本,我希望看到立即打印3个块(在看到延迟之前,ProcessPoolExplorer初始化时使用的3个工作进程各一个。我不确定为什么您的第一个解决方案没有出现这种情况。感谢您在这方面的帮助,我重新思考了这个问题,我相信我对导致原始问题的原因有了更好的解释。请参阅更新的答案。
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
def print_something(thing):
# NOT cpu-intensive, so threads should work well here
print(thing)
time.sleep(10)
return thing # so there is a non-None result
# define a generator
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
def main():
chunk_number = 0
alphabet = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
futures = {}
with ThreadPoolExecutor(max_workers=26) as executor:
for current_chunk in chunks(alphabet, 3): # Restrict to calling the function 3 times per second
futures.update({executor.submit(print_something, thing): thing for thing in current_chunk })
chunk_number += 1
print('chunk %s' % chunk_number)
time.sleep(1)
# needs to be within the executor block else it won't run until all futures are complete
for result in as_completed(futures):
print(result.result())
if __name__ == '__main__':
main()