13.2. AsyncIO About¶
asyncio
in Python standard libraryasync
andawait
builtin keywordsRunning asynchronously: 3s + 1s + 1s = bit over 3s [execution time]
Async is the future of programming
13.2.1. Advantages¶
Maximize the usage of a single thread
Handling I/O asynchronously
Enabling concurrent code using coroutines
Async will fill the gaps, otherwise wasted on waiting for I/O
You control when tasks switches occur, so locks and other synchronization are no longer needed
Async is the cheapest way to task switch
Cost task switches is incredibly low; calling a pure Python function has more overhead than restarting a generator or awaitable
Function builds stack each time it's called, whereas async uses generators underneath, which already has stack created
In terms of speed async servers blows threaded servers in means of thousands
Async is very cheap in means of resources
Async world has a huge ecosystem of support tools
Coding is easier to get right, than threads
13.2.2. Disadvantages¶
Async switches cooperatively, so you do need to add explicit code
yield
orawait
to cause a task to switchEverything you do need a non-blocking version (for example
open()
)Increased learning curve
Create event loop, acquire, crate non-blocking versions of your code
You think you know Python, there is a second half to learn (async)
13.2.3. Sync vs Async¶



Figure 13.10. Source: Langa, Ł. import asyncio: Learn Python's AsyncIO [4]¶

Figure 13.11. Source: Langa, Ł. import asyncio: Learn Python's AsyncIO [4]¶
13.2.4. Execution¶


13.2.5. Example¶
>>> import asyncio
>>>
>>>
>>> async def a():
... print('a: started')
... await asyncio.sleep(0.2)
... print('a: finished')
... return 'a'
>>>
>>> async def b():
... print('b: started')
... await asyncio.sleep(0.1)
... print('b: finished')
... return 'b'
>>>
>>> async def c():
... print('c: started')
... await asyncio.sleep(0.3)
... print('c: finished')
... return 'c'
>>>
>>> async def main():
... result = await asyncio.gather(a(), b(), c())
... print(f'Result: {result}')
>>>
>>>
>>> asyncio.run(main())
a: started
b: started
c: started
b: finished
a: finished
c: finished
Result: ['a', 'b', 'c']
13.2.6. Further Reading¶
13.2.7. References¶
13.2.8. Assignments¶
"""
* Assignment: OOP Async Coroutine
* Complexity: easy
* Lines of code: 2 lines
* Time: 2 min
English:
1. Define:
a. coroutine function `a()`
2. After running coroutine should:
b. return 'a'
3. Run doctests - all must succeed
Polish:
1. Zdefiniuj:
a. coroutine function `a()`
2. Po uruchomieniu coroutine powinna:
a. zwracać 'a'
3. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import iscoroutine, iscoroutinefunction
>>> import asyncio
>>> assert iscoroutinefunction(a)
>>> assert iscoroutine(a())
>>> asyncio.run(a())
'a'
"""
# Coroutine function `a()`
# Function should return 'a'
# type: Coroutine
def a():
...
"""
* Assignment: OOP Async Sleep
* Complexity: easy
* Lines of code: 3 lines
* Time: 2 min
English:
1. Define:
a. coroutine function `a()`
2. After running coroutine should:
a. wait for 1.0 seconds
b. return 'a'
3. Run doctests - all must succeed
Polish:
1. Zdefiniuj:
a. coroutine function `a()`
2. Po uruchomieniu coroutine powinna:
a. czekać 1.0 sekundę
b. zwracać 'a'
3. Uruchom doctesty - wszystkie muszą się powieść
Hint:
* asyncio.sleep()
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import iscoroutine, iscoroutinefunction
>>> import asyncio
>>> assert iscoroutinefunction(a)
>>> assert iscoroutine(a())
>>> asyncio.run(a())
'a'
"""
import asyncio
# coroutine function `a()`
# wait for 1.0 seconds, return 'a'
# type: Coroutine
def a():
...
"""
* Assignment: OOP Async Concurrent
* Complexity: easy
* Lines of code: 15 lines
* Time: 5 min
English:
1. Define:
a. coroutine function `a()`
b. coroutine function `b()`
c. coroutine function `c()`
2. After running coroutine should:
a. print 'NAME: before'
b. wait for X seconds
c. print 'NAME: after'
d. return NAME, where
3. Definition of NAME:
a. for coroutine `a()`, NAME is 'a'
a. for coroutine `b()`, NAME is 'b'
a. for coroutine `c()`, NAME is 'c'
4. Definition of X:
a. for coroutine `a()`, X is 1.0
a. for coroutine `b()`, X is 0.5
a. for coroutine `c()`, X is 1.5
3. Run doctests - all must succeed
Polish:
1. Zdefiniuj:
a. coroutine function `a()`
b. coroutine function `b()`
c. coroutine function `c()`
2. Po uruchomieniu coroutine powinna:
a. wyświetlić 'NAME: before'
b. czekać X sekund
c. wyświetlić 'NAME: after'
d. zwrócić NAME
3. Definicja NAME:
a. dla coroutine `a()`, NAME to 'a'
a. dla coroutine `b()`, NAME to 'b'
a. dla coroutine `c()`, NAME to 'c'
3. Definicja X:
a. dla coroutine `a()`, X to 1.0
a. dla coroutine `b()`, X to 0.5
a. dla coroutine `c()`, X to 1.5
3. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import iscoroutine, iscoroutinefunction
>>> import asyncio
>>> assert iscoroutinefunction(a)
>>> assert iscoroutinefunction(b)
>>> assert iscoroutinefunction(c)
>>> assert iscoroutine(a())
>>> assert iscoroutine(b())
>>> assert iscoroutine(c())
>>> async def main():
... return await asyncio.gather(a(), b(), c())
>>>
>>> asyncio.run(main())
a: before
b: before
c: before
b: after
a: after
c: after
['a', 'b', 'c']
"""
import asyncio
# coroutine function `a()`
# print 'a: before', wait 1.0 second, print 'a: after', return 'a'
# type: Coroutine
def a():
...
# coroutine function `b()`
# print 'b: before', wait 0.5 second, print 'b: after', return 'b'
# type: Coroutine
def b():
...
# coroutine function `c()`
# print 'c: before', wait 1.5 second, print 'c: after', return 'c'
# type: Coroutine
def c():
...