#python #python-asyncio
#python #python-asyncio
Вопрос:
Я реализовал простой класс с именем Saver, который имеет две функции, которые могут выполнять его вызывающие. Это save_all() и save_one()
Как следует из названия, когда я выполняю save_all() , я хочу, чтобы он выполнял функцию save_one() в цикле. Я просто хочу убедиться, что только один вызывающий может выполнить save_all() или save_one() в данный момент времени извне.
import asyncio
class Saver:
def __init__(self):
# it's lock
self.lock = asyncio.Lock()
def save_all():
yield from self.lock.acquire()
for i in range(3):
save_one(i)
self.lock.release()
def save_one():
yield from self.lock.acquire()
print(i)
self.lock.release()
Теперь происходит то, что если я вызываю save_all() , он получает блокировку, а оператор save_one() print() никогда не выполняется.
Как мне убедиться, что только вызывающий может вызывать либо save_all(), либо save_one() извне:
saver = Saver()
saver.save_all()
Комментарии:
1. Либо координируйте с отдельным флагом, который
save_one
не должен пытаться получить другую блокировку, либо используйте отдельные блокировки дляsave_one
andsave_all
.2.
save_all
Действительно ли нужна блокировка? Кроме того, вы, вероятно, захотите использоватьthreading.Lock
, если вы не используете блокировки внутри сопрограмм asyncio.3. @dirn
threading.Lock
— неправильный инструмент, потому что он заблокирует цикл событий во время ожидания получения блокировки (возможно, вызывая взаимоблокировку). Похоже, что OP использует блокировку в коде asyncio, по-видимому, используяyield from
синтаксис.4. @user4815162342 но, похоже, OP не использует
coroutine
декоратор.5. @dirn Это хороший момент, но, с другой стороны, OP опускает
self
список параметров, но использует его в телах функций, вызывает методы, как если бы они были функциями верхнего уровня, и так далее. Показанный код, по-видимому, является лишь приближением к фактическому коду OP, который, как я ожидаю, будет включать декоратор, потому что он скопирован из устаревшего руководства.
Ответ №1:
Ввод печати save_one()
не выполняется в коде, как показано, потому что код никогда не ожидает save_one()
, он просто вызывает его. (Но у кода есть и другие тривиальные недостатки, поэтому, возможно, он больше похож на псевдокод.) Также обратите внимание, что сопрограммы yield-from устарели, поэтому вы должны использовать async def
and await
вместо def
and yield from
при написании async Python.
Поскольку asyncio не поставляется с реентерабельной блокировкой, самый простой способ устранить проблему — заблокировать только точки входа API. Базовые методы реализации могут полностью избежать блокировки, поскольку об этом заботятся на границах системы. Тогда взаимоблокировки не будет, потому что internal _save_all
вызовет internal _save_one
, и блокировка будет получена только один раз (in save_call
, перед вызовом _save_all
, или in save_one
, перед вызовом _save_one
, в зависимости от обстоятельств):
class Saver:
def __init__(self):
self.lock = asyncio.Lock()
async def save_all(self):
async with self.lock:
await self._save_all()
async def save_one(self):
async with self.lock:
await self._save_one()
async def _save_all(self):
# private method, must be invoked with the lock held
for i in range(3):
await self._save_one(i)
async def _save_one(self, i):
# private method, must be invoked with the lock held
print(i)