Python 寻找更优雅的任务处理解决方案

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

我正在研究任务处理解决方案。任务源是具有数千条记录的SQLlite DB。每个任务都是http请求,因此可能需要几秒钟才能完成。我决定使用asyncio进行处理。示例基于小任务队列,所以“按原样”使用它们非常消耗内存,并且需要花费大量时间来填充任务列表。 在文档中,它看起来像这样


    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信号允许在收集之前填充任务列表,事实上,这正是第一个代码片段所做的。向第二个解决方案添加信号量没有任何意义,因为该代码的体系结构已经确保了最大数量的工作人员。