什么是Python asyncio.Lock()?

use*_*759 17 python python-asyncio

是因为协程可能会在未来被抢占吗?或者它允许人们使用关键部分的收益率(不应该鼓励IMO)?

dan*_*ano 36

您使用它的原因与在线程代码中使用锁定的原因相同:保护关键部分.asyncio主要用于单线程代码,但仍然会发生并发执行(任何时候你点击yield fromawait),这意味着有时你需要同步.

例如,考虑一个从Web服务器获取某些数据的函数,然后缓存结果:

@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
Run Code Online (Sandbox Code Playgroud)

现在假设您有多个并发运行的协同例程可能需要使用以下的返回值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))
Run Code Online (Sandbox Code Playgroud)

现在,假装从中获取数据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
Run Code Online (Sandbox Code Playgroud)

另外需要注意的是,当一个协同程序在内部get_stuff,进行aiohttp调用,另一个stuff_lock协同程序等待时,第三个协同程序根本不需要调用get_stuff也可以运行,而不受协同程序阻塞的影响Lock.

显然这个例子有点做作,但希望它能让你知道为什么asyncio.Lock有用; 它允许您保护关键部分,而不会阻止其他协同程序运行,而不需要访问该关键部分.

  • 谢谢你的详细解释.让我总结一下(对于那些不理解这个问题的人).1)协同程序不能被抢占 - 它们一直运行直到"yield from"将控制权返回到循环.2)asyncio.Lock()用于保护调用"yield from"的关键部分 - 否则无需使用锁.讨论是在单线程asyncio使用模式的假设下进行的. (5认同)
  • @NickChammas如果整个应用程序中运行的唯一协程都在get_stuff上等待,那么性能将是相同的。但是,如果您有其他协程运行不需要锁的其他方法,则不会。阻止的HTTP请求将阻止所有其他协程,从而完全停止您的应用程序直到完成。使用`asyncio.Lock`允许所有其他协同程序继续运行。 (4认同)
  • dano - 在这个特定的示例中(请记住,它确实是人为的),如果您只是删除“yield from”并发出阻塞 HTTP 请求,您不会获得相同的性能吗?因为在锁定的情况下,只有 1 个 `get_stuff()` 实例正在进行中。(例如,实例 B 仅当实例 A 完全完成后才能启动。) (3认同)

小智 5

一个例子是,当您只希望某些代码运行一次,但许多代码都要求运行时(例如在网络应用程序中)

async def request_by_many():
    key = False
    lock = asyncio.Lock()
    async with lock:
        if key is False:
            await only_run_once()

async def only_run_once():
    while True:
        if random()>0.5:
            key = True
            break
        await asyncio.sleep(1)
Run Code Online (Sandbox Code Playgroud)

  • 我认为要实现此功能,您只需要实际实例化一次“asyncio.Lock”对象,并将其存储在每个调用者都可以访问的位置,例如作为某个共享对象上的属性。照原样,每个调用都会实例化一个新锁并继续执行,就好像它不存在一样。 (2认同)