Реализация механизма переключения контекста в asyncioPython

Программы на Python
Ответить
Anonymous
 Реализация механизма переключения контекста в asyncio

Сообщение Anonymous »

Я хочу создать что-то похожее на механизм переключения контекста, который позволит использовать общий ресурс по одному.
В моем случае это один объект сеанса, подключающийся к веб-сайту, и каждый контекст будет текущей учетной записью, подключенной к веб-сайту. В качестве системного ограничения - никакие две учетные записи не могут быть подключены одновременно, а подключение является дорогостоящей операцией.
Я разработал следующий механизм, который предполагает отпускание контекста (переключение аккаунт) во время дорогостоящих фоновых операций:

Код: Выделить всё

counter = 0  # i.e. ContextVar for current account
def increase_counter():
global counter
counter += 1

def decrease_counter():
global counter
counter -= 1

async def run_operation():
while True:
operation = operation_queue.get()
increase_counter()

task = asyncio.create_task(operation())
task.add_done_callback(lambda fut: decrease_counter())

await wait_for_free() # wait until counter == 0
switch_context()  # Log in to a different account

async def operation():
# do stuff
decrease_counter()
sleep(60) # Long background operation
await wait_for_context()  # Wait for context to come back and be 1.
# continue
Когда цепочка операций всего одна, этот механизм работает хорошо. Внутренние функции всегда могут освободить счетчик и вернуть его после операции.
К сожалению, когда одновременно выполняются две операции, он перестает работать:

Код: Выделить всё

async def operation():
increase_counter()
task1 = asyncio.create_task(sub_op())
task1.add_done_callback(decrease)
increase_counter()
task2 = asyncio.create_task(sub_op())
task2.add_done_callback(decrease)

decrease_counter()
await asyncio.gather(task1, task2)
await wait_for_context()

async def sub_op():
decrease_counter()
sleep(60)
await wait_for_context()
Счетчик в этом случае увеличивается следующим образом:

Код: Выделить всё

run_op (+1 = 1)
task1_creation (+1 = 2)
task2_creation (+1 = 3)
gather_release (-1 = 2)
task1_sleep (-1 = 1)
task2_sleep (-1 = 0)  # Context released
task1_resume (+1 = 1)
task2_resume (+1 = 2)
task1_done_callback (-1 = 1)
task2_done_callback (-1 = 0)  # Context needlessly released
gather_resume (+1 = 1)
Можно ли предотвратить ненужный выпуск или это просто факт жизни при использовании такого механизма? Если да, то существует ли какой-либо другой механизм, который может решить проблему, кроме использования счетчика?

Подробнее здесь: https://stackoverflow.com/questions/790 ... in-asyncio
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»