$30 off During Our Annual Pro Sale. View Details »

Asyncio - understanding async and await in Python

Asyncio - understanding async and await in Python

Jonathan Slenders

February 01, 2020
Tweet

More Decks by Jonathan Slenders

Other Decks in Programming

Transcript

  1. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential • Multithreading • Multiprocessing • Fibers / green threads: • gevent • Event loop • Twisted (2002) • Tornado • Asyncio (2012) • Curio • Trio Concurrency in Python
  2. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Let’s start with threads
  3. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Let’s start with threads
  4. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Let’s start with threads
  5. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Let’s start with threads
  6. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Let’s start with threads
  7. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential • Synchronization is required when accessing shared data structures • Choosing the right locking granularity is hard. • Risk of deadlocking. • (Theads have some overhead: memory, context switching.) -> Instead of using locks, people often use queues (message passing) for inter-thread communication. What’s wrong with threads?
  8. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Await -> Checkpoint where it’s safe for asyncio to go to another coroutine
  9. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Threads vs coroutines Threads Coroutines Pre-emptive: The operating system decides when to context switch to another task Co-operative: The tasks themselves decide when to hand over control. Can switch to other thread at any point in time. Only switches to other coroutine when there’s an “await” Tell when it’s impossible to go to another thread (using locks). Tell when it’s possible to go to another thread (using await).
  10. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential • Dispatch table An abstraction on top of event loops I/O completion event Callback File 1 ready for reading func1 Stdin ready for reading func2 Network socket 1 ready for writing func3 Received mouse event func4 Received keyboard event func5 Etc…
  11. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential while True: wait_for_any_fd_to_become_ready() handle_fd_callback() An abstraction on top of event loops
  12. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Event loops: The good things - Everything runs in one thread: - But only one callback at a time. - No complicated synchronization (data locking) - Little risk of deadlocking. - Easy to debug. - Handle many connections in parallel. - Idle connections barely consume anything in an event driven system. - Cheaper then one thread per connection.
  13. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential while True: wait_for_any_fd_to_become_ready() handle_fd_callback() Don’t mix with blocking I/O - Callbacks can’t do any kind of blocking I/O, like `read()`, `recv()`, etc… - Instead, they should do it asynchronously, and register the file descriptor with a callback in the event loop.
  14. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Run a coroutine Tell asyncio: While we wait for the response, feel free to do other stuff in the meantime
  15. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Run two coroutines in parallel
  16. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential Start coroutine without waiting
  17. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential • Imagine logging to a remote server. • Logging can eventually happen in every function. • So, every function needs to become async. No: use an asynchronous queue: - On one end, push the messages into the queue (queue.put_nowait) - On the other end, have one coroutine consume the queue and flush to the remote server. Don’t turn every call into an async call
  18. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential • IPython • python –m asyncio (for Python 3.8+) Asyncio REPL
  19. © 2018 Cisco and/or its affiliates. All rights reserved. Cisco

    Confidential • Asyncio is a great concurrency pattern for I/O heavy applications. • Not the easiest to begin with, but when things become complex, often easier than threading • Important pitfalls: • Don’t mix with blocking I/O. • Don’t turn every function into an async function. Conclusion jonathan_s jonathanslenders