Python 寻找更优雅的任务处理解决方案
我正在研究任务处理解决方案。任务源是具有数千条记录的SQLlite DB。每个任务都是http请求,因此可能需要几秒钟才能完成。我决定使用asyncio进行处理。示例基于小任务队列,所以“按原样”使用它们非常消耗内存,并且需要花费大量时间来填充任务列表。 在文档中,它看起来像这样Python 寻找更优雅的任务处理解决方案,python,concurrency,python-asyncio,Python,Concurrency,Python Asyncio,我正在研究任务处理解决方案。任务源是具有数千条记录的SQLlite DB。每个任务都是http请求,因此可能需要几秒钟才能完成。我决定使用asyncio进行处理。示例基于小任务队列,所以“按原样”使用它们非常消耗内存,并且需要花费大量时间来填充任务列表。 在文档中,它看起来像这样 tasks = [] for i in range(1,10): task = asyncio.create_task(worker(i)) tasks
tasks = []
for i in range(1,10):
task = asyncio.create_task(worker(i))
tasks.append(task)
await asyncio.gather(tasks)
我想做的是从数据库中逐个读取任务,并对其进行处理,使并发性保持在MAX_CONCURRENT的限制内
所以,这里是我的肮脏的黑客,但我相信有一个更优雅的解决方案
UPD
早上一小时抵得上晚上两小时:)
但无论如何,我认为使用信号量会更好,但我不确定如何在循环中使用它
随机导入
导入异步
进口aiohttp
从aiohttp导入客户端会话
从sqlitedict导入sqlitedict
异步def testWorker(id、url、db):
#占位符url处理
等待asyncio.sleep(random.randint(1,5))
异步def main():
最大并发=5
db=SqliteDict('./taskdb.sqlite',autocommit=True)
tasks=set()
it=db.iteritems()
尽管如此:
尝试:
id,url=next(it)
如果(len(tasks)
正如您所发现的,最惯用的方法是使用。在这种情况下,您的循环变得简单得多,因为它不再需要强制执行MAX\u CONCURRENT
,而是由信号量完成:
async def testWorker(id,url, db, semaphore):
# async with ensures that no more than MAX_CONCURRENT
# workers enter the block at the same time
async with semaphore:
await asyncio.sleep(random.randint(1,5))
async def main():
semaphore = asyncio.Semaphore(MAX_CONCURRENT)
db = SqliteDict('./taskdb.sqlite', autocommit=True)
async with ClientSession() as session:
coros = [testTask(id, url, db, semaphore)
for id, url in db.iteritems()]
results = await asyncio.gather(*tasks)
另一种选择是启动固定数量的工人,并通过网络与他们沟通。这稍微复杂一些,但对于任务数量可能很大或无限的情况,这是一个很好的选择
async def testWorker(db, queue):
while True:
id, url = queue.get()
await asyncio.sleep(random.randint(1,5))
queue.task_done()
async def main():
queue = asyncio.Queue()
workers = [asyncio.create_task(db, queue)]
db = SqliteDict('./taskdb.sqlite', autocommit=True)
async with ClientSession() as session:
for id, url in db.iteritems():
await queue.put((id, url))
await queue.join()
# cancel the workers, which are now sitting idly
for w in workers:
w.cancel()
非常感谢。这是一个很好的解决方案。带有信号量的示例是最好的,但需要在收集之前填写任务列表。据我所知,只需在第二个示例中添加信号量就可以实现最大的工作负载。类似于
python async def testWorker(db,queue,semaphore):True:async with semaphore:id,url=queue.get()wait asyncio.sleep(random.randint(1,5))queue.task_done()
@TimRulanov信号允许在收集之前填充任务列表,事实上,这正是第一个代码片段所做的。向第二个解决方案添加信号量没有任何意义,因为该代码的体系结构已经确保了最大数量的工作人员。