Что Python asyncio.Lock() для?

Это потому, что сопрограммы могут быть выгружены в будущем? Или это позволяет людям использовать доходность в критическом разделе (что ИМО не следует поощрять)?

Ответ 1

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

Например, рассмотрим функцию, которая извлекает некоторые данные с веб-сервера, а затем кэширует результаты:

@asyncio.coroutine
def get_stuff(url):
    if url in cache:
        return cache[url]
    stuff = yield from aiohttp.request('GET', url)
    cache[url] = stuff
    return stuff

Теперь предположим, что вы одновременно используете несколько совлокальных подпрограмм, которые потенциально могут использовать возвращаемое значение get_stuff:

def parse_stuff():
    stuff = yield from get_stuff()
    # do some parsing

def use_stuff():
    stuff = yield from get_stuff()
    # use stuff to do something interesting

def do_work():
     out = yield from aiohttp.request("www.awebsite.com")
     # do some work with out


tasks = [
     asyncio.async(parse_stuff()),
     asyncio.async(use_stuff()),
     asyncio.async(do_work()),
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

Теперь сделайте вид, что выборка данных из url выполняется медленно. Если одновременно выполняются как parse_stuff, так и use_stuff, каждый из них получит полную стоимость перехода по сети, чтобы получить stuff. Если вы защитите метод с помощью блокировки, вы избегаете этого:

stuff_lock = asyncio.Lock()

def get_stuff(url):
    with (yield from stuff_lock):
        if url in cache:
            return cache[url]
        stuff = yield from aiohttp.request('GET', url)
        cache[url] = stuff
        return stuff

Еще одна вещь, которую следует отметить, состоит в том, что, хотя одна сопрограмма находится внутри get_stuff, вызывая вызов aiohttp, а другой ждёт на stuff_lock, третья сопрограмма, которая не требует вызова get_stuff вообще, может также выполняться, не подвергая блокировку сопрограммы coroutine на Lock.

Очевидно, что этот пример немного надуман, но, надеюсь, он дает вам представление о том, почему asyncio.Lock может быть полезным; он позволяет защитить критический раздел, не блокируя выполнение других сопрограмм, которые не нуждаются в доступе к этому критическому разделу.