Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what's Python asyncio.Lock() for?

Is it because coroutines may be preempted in the future? Or it allows people to use yield from in critical section (which IMO shouldn't be encouraged)?

like image 954
user3761759 Avatar asked Sep 12 '14 01:09

user3761759


People also ask

How do you use locks in Python?

A lock can be locked using the acquire() method. Once a thread has acquired the lock, all subsequent attempts to acquire the lock are blocked until it is released. The lock can be released using the release() method. Calling the release() method on a lock, in an unlocked state, results in an error.

Is async IO faster than threads?

One of the cool advantages of asyncio is that it scales far better than threading . Each task takes far fewer resources and less time to create than a thread, so creating and running more of them works well. This example just creates a separate task for each site to download, which works out quite well.

What is async await used for Python?

The async/await keywords The async keyword is used to create a Python coroutine. The await keyword suspends execution of a coroutine until it completes and returns the result data. The await keywords only works within an async function.


2 Answers

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.

like image 199
dano Avatar answered Oct 01 '22 03:10

dano


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) 
like image 33
pumpkindle Avatar answered Oct 01 '22 02:10

pumpkindle