Python Asyncio Patterns
Asyncio patterns are the queue, lock, and event tools that coroutines share.
Put and get with a queue
You await both put() and get() on an asyncio queue.
import asyncio
async def main():
queue = asyncio.Queue()
await queue.put("job")
item = await queue.get()
print(item)
asyncio.run(main())Hold a lock with async with
async with holds the lock for the block. It releases the lock when the block ends.
import asyncio
async def main():
lock = asyncio.Lock()
async with lock:
print("inside lock")
asyncio.run(main())Wait for an event
wait() pauses until another coroutine sets the event. Here the event is already set.
import asyncio
async def main():
event = asyncio.Event()
event.set()
await event.wait()
print("ready")
asyncio.run(main())Run a blocking call aside
run_in_executor() runs a normal function in a worker thread. The loop stays free while it runs.
import asyncio
def blocking():
return "file read"
async def main():
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(None, blocking)
print(result)
asyncio.run(main())Example
import asyncio
async def producer(queue):
for word in ["pear", "apple", "kiwi"]:
await queue.put(word)
await queue.put(None)
async def consumer(queue, results):
while True:
item = await queue.get()
if item is None:
break
results.append(item.upper())
async def main():
queue = asyncio.Queue()
results = []
prod = asyncio.create_task(producer(queue))
cons = asyncio.create_task(consumer(queue, results))
await prod
await cons
for word in sorted(results):
print(word)
asyncio.run(main())Key points
- Use asyncio.Queue to pass items between coroutines.
- Use await queue.put() to add an item.
- Use await queue.get() to take the next item.
- Use asyncio.Lock to guard shared data between coroutines.
- Use async with on a lock to hold it and release it.
- asyncio.Event: a flag that coroutines can wait for and set.
- Use run_in_executor() to run a blocking call outside the loop.
- asyncio queues and locks belong to one event loop.
- Queues, locks and events are the shared tools that coroutines work with.
In this example, the producer puts three words on the queue. It then puts None as a sentinel. A sentinel is a special value that means the work is done. The consumer takes items until it sees None. It stores each word in upper case. main() awaits both tasks and then prints the results sorted. Sorting makes the report order fixed.
Note
Use queue.Queue to share work between threads.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Python Playground
Exercise
Why does the producer put None on the queue at the end?