我想对API进行数千次调用,这种调用有点慢 - 几秒钟才能得到响应。唯一的限制是我每秒最多可以提出一个请求。最好的方法是什么?我认为以下代码有效,但我觉得我应该能够以某种方式更好地利用线程库。我正在使用python 3.3
last_job = datetime.now()
for work in work_list:
while (datetime.now()-last_job).total_seconds() < 1 or threading.active_count() >= max_threads:
time.sleep(.1)
threading.Thread(target=work_function, args=[work]).start()
last_job = datetime.now()
答案 0 :(得分:14)
如果要使用固定大小的线程池运行一堆作业,可以使用concurrent.futures.ThreadPoolExecutor
,如下所示:
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=5) as executor:
for work in work_list:
executor.submit(work_function, work)
如果您想确保每秒最多拨打一次API,那么您需要在work_function
内执行此操作。提交作业时无法执行此操作,因为您不知道作业排队等待线程可用的时间。
如果是我,我会将速率限制代码放入自己的类中,以便它可以重复使用:
from collections import Iterator
from threading import Lock
import time
class RateLimiter(Iterator):
"""Iterator that yields a value at most once every 'interval' seconds."""
def __init__(self, interval):
self.lock = Lock()
self.interval = interval
self.next_yield = 0
def __next__(self):
with self.lock:
t = time.monotonic()
if t < self.next_yield:
time.sleep(self.next_yield - t)
t = time.monotonic()
self.next_yield = t + self.interval
api_rate_limiter = RateLimiter(1)
def work_function(work):
next(api_rate_limiter)
call_api(...)
在Python 3.3中引入了 time.monotonic
;在旧版本的Python中,您可以使用time.time
,但是当系统时钟发生变化时,这可能会向后跳转,因此您需要确保这不会导致睡眠过长:
time.sleep(min(self.next_yield - t, self.interval))