#python #python-asyncio
#python #python-asyncio
Вопрос:
У меня есть долго работающая функция, которая вызывается несколько раз for
циклом с разными параметрами.
Я хочу оценить asyncio
библиотеку, чтобы легко увеличить скорость этой функции.
Функция возвращает определенное значение, которое должно храниться на уровне for
цикла в списке resultList
.
Я уже нашел несколько фрагментов кода, которые, похоже, соответствуют моему требованию «простоты реализации» (мне просто нужно добавить @background
декоратор), но мне не хватает двух функций:
- как дождаться на уровне
for
цикла, пока не будут выполнены все выполнения функции? - как собрать результаты вызова функции на уровне
for
цикла?
Как мне расширить этот фрагмент кода?
import asyncio
import time
def background(f):
def wrapped(*args, **kwargs):
return asyncio.get_event_loop().run_in_executor(None, f, *args, **kwargs)
return wrapped
@background
def your_function(argument):
time.sleep(5)
myReturn = 'function finished for ' str(argument)
print(myReturn)
return myReturn
if __name__ == '__main__':
resultList = []
for i in range(10):
your_function(i)
# resultList.append()
# loop = asyncio.get_event_loop().run_until_complete()
print('this should only be printed once all calls to your_function() are finished')
print("resultList=", resultList) # should contain a list of entries with 'function finished for x'
Комментарии:
1. Ограничен ли ваш процесс ввода-вывода? В противном
asyncio
случае это вообще не поможет, поскольку GIL все еще активен, и только один поток может обрабатывать код Python одновременно. Фрагмент кода, похоже, вообще не выиграет от этого. Я знаю, что это не отвечает на вопрос, но я бы хотел, чтобы вы не шли по ненужному пути.2. Спасибо, что ознакомили с этой темой. Моя функция ввода-вывода ограничена, да.
Ответ №1:
Есть несколько проблем, которые я вижу с кодом, которым вы поделились. Примечание: мой ответ основан на вашем комментарии к Jan, где вы упоминаете, что ваша функция ограничена вводом-выводом.
- Не используйте
asyncio.get_event_loop().run_in_executor()
, используйтеasyncio.run()
в своей точке входа и сохраняйте свои методы полностью асинхронными, если можете. (https://docs.python.org/3/library/asyncio-task.html#asyncio.run ) - Вы используете
time.sleep()
. Это синхронная версия sleep, которая не будет имитировать asyncio. Используйтеasyncio.sleep()
вместо этого. - Обратите внимание, что
run_in_executor
возвращает объект future, который вы хотитеawait
. Поскольку вы запускаете свой код в синхронной функции, это более сложно, чем запускать все это из асинхронной функции.
Теперь, посмотрев на код, которым вы поделились, нет причин преобразовывать его в стек asyncio
. Если есть другие внешние ограничения или это просто фрагмент более крупной программы, решение, приведенное ниже, может быть немного другим. В противном случае приведенное ниже решение является простым.
import asyncio
async def your_function(argument):
await asyncio.sleep(5) # note the await here (compared to time.sleep())
my_return = f"function finished for {argument}"
print(my_return)
return my_return
async def async_main():
result_list = await asyncio.gather(*(range(10))) # gather will run it all concurrently
print("this should only be printed once all calls to your_function() are finished")
print(f"result_list={result_list}", result_list) # should contain a list of entries with 'function finished for x'
if __name__ == '__main__':
asyncio.run(async_main())