Merge branch 'master' of github.com:Brandon-Rozek/website

This commit is contained in:
Brandon Rozek 2020-04-11 20:12:35 -04:00
commit 8dfaeb8cba
2 changed files with 96 additions and 0 deletions

33
content/blog/gevent.md Normal file
View file

@ -0,0 +1,33 @@
---
title: "Gevent"
date: 2020-04-09T17:22:52-04:00
draft: false
tags: ["python", "concurrency"]
---
In my last post I spoke about [concurrency with asyncio](https://brandonrozek.com/blog/pyasyncio/). Now what if you don't want to concern yourself with async/await practices and just want to write synchronous code that executes I/O asynchronously? That's where the library [gevent](http://www.gevent.org/) comes in. It does this by modifying Python's standard library during runtime to call it's own asynchronous versions.
Last post code's example written in `gevent`.
```python
# The first two lines must be called before
# any other modules are loaded
import gevent
from gevent import monkey; monkey.patch_all()
import time
def think(duration):
print("Starting to think for " + str(duration) + " seconds...")
time.sleep(duration)
print("Finished thinking for " + str(duration) + " seconds...")
gevent.wait([
gevent.spawn(think, 5),
gevent.spawn(think, 2)
])
```
Notice that the function `think` is written the same as the synchronous version.
`gevent` is written on top of C libraries `libev` or `libuv` . This combined with the monkey patching can make `gevent` based applications hard to debug if something goes wrong. Otherwise it's a great tool to quickly take advantage of concurrency.

63
content/blog/pyasyncio.md Normal file
View file

@ -0,0 +1,63 @@
---
title: "Python asyncio"
date: 2020-04-09T16:37:41-04:00
draft: false
tags: ["python", "concurrency"]
---
Daniel Pope wrote a [great blog post](http://mauveweb.co.uk/posts/2014/07/gevent-asynchronous-io-made-easy.html) describing the different ways of performing asynchronous I/O in Python. In this post, I want to focus on his section called "Generator-based Coroutine". Python's `asyncio` module in the standard library has a concept of "coroutines" that uses generators instead of callbacks or promises seen in other asynchronous frameworks.
Whenever a I/O call is made, you can prepend the statement with `yield from` and as long as the function is under the `asyncio.coroutine` generator, the Python module will handle the call asynchronously. Now this method doesn't fully fit under the coroutine framework since generators can only yield to the caller frame. Therefore, it is called a [semicoroutine](https://en.wikipedia.org/wiki/Coroutine#Comparison_with_generators).
Here is an example of its usage
```python
import asyncio
@asyncio.coroutine
def think(duration):
print("Starting to think for " + str(duration) + " seconds...")
yield from asyncio.sleep(duration)
print("Finished thinking for " + str(duration) + " seconds...")
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
think(5),
think(2)
))
loop.close()
```
Output:
```
Starting to think for 5 seconds...
Starting to think for 2 seconds...
Finished thinking for 2 seconds...
Finished thinking for 5 seconds...
```
## Newer Syntax
Now the decorator syntax has been deprecated in Python 3.8 in favor of the more common `async` /`await` syntax introduced in Python 3.7. The reason I showed the previous version is because I think it's important to understand how this module is implemented behind the scenes. Also since Python 3.6 code is likely still floating around, you might encounter code bases like the above in your day to day.
Here is the equivalent code written in modern day syntax
```python
import asyncio
async def think(duration):
print("Starting to think for " + str(duration) + " seconds...")
await asyncio.sleep(duration)
print("Finished thinking for " + str(duration) + " seconds...")
async def main():
await asyncio.gather(
think(2),
think(5)
)
asyncio.run(main())
```
Not that much shorter in terms of lines of code, but it allows the developer to not have to be concerned about the event loop or generators.