what's Python asyncio.Lock() for?

You use it for the same reason you'd use a lock in threaded code: to protect a critical section. asyncio is primarily meant for use in single-threaded code, but there is still concurrent execution happening (any time you hit a yield from or await), which means sometimes you need synchronization.

For example, consider a function that fetches some data from a web server, and then caches the results:

async def get_stuff(url):
    if url in cache:
        return cache[url]
    stuff = await aiohttp.request('GET', url)
    cache[url] = stuff
    return stuff

Now assume that you've got multiple co-routines running concurrently that might potentially need to use the return value of get_stuff:

async def parse_stuff():
    stuff = await get_stuff("www.example.com/data")
    # do some parsing

async def use_stuff():
    stuff = await get_stuff("www.example.com/data")
    # use stuff to do something interesting

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

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
    parse_stuff(),
    use_stuff(),
    do_work(),
))

Now, pretend that fetching data from url is slow. If both parse_stuff and use_stuff run concurrently, each will be hit with the full cost of going over the network to fetch stuff. If you protect the method with a lock, you avoid this:

stuff_lock = asyncio.Lock()

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

One other thing to note is that while one coroutine is inside get_stuff, making the aiohttp call, and another waits on stuff_lock, a third coroutine that doesn't need to call get_stuff at all can also be running, without being affected by the coroutine blocking on the Lock.

Obviously this example is a little bit contrived, but hopefully it gives you an idea of why asyncio.Lock can useful; it allows you to protect a critical section, without blocking other coroutines from running which don't need access to that critical section.


one example is when you only want some code run once, yet is requested by many(when in a web app for example)

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)