# [[concurrency|Concurrency]] and [[async|Async Programming]] in [[python|Python]]
- [[thread|Threading]], [[process]]es, and coroutines.
- [Asyncio | Pysheeet](https://www.pythonsheets.com/notes/python-asyncio.html)
## Threading
- With `threading` library, real OS level threads are created, but GIL explicitly prevents multiple threads from executing Python bytecode at the same time.
- GIL will suspend the thread every 5ms.
- Delegating synchronous tasks to thread with `asyncio.to_thread`
## `asyncio`
- `coroutine`
- `async def` functions (most likely with `await` inside) are coroutine functions and return coroutine objects.
- `coroutine` is an object that encapsulates the ability to resume (by `await`) an underlying function that has been suspended before completion.
- Coroutines can either be `await`-ed, or be ran by putting into event loop. `asyncio.run()`, `loop.run_until_complete()`, `asyncio.create_task() -> Task`
- Inside a coroutine, `asyncio.sleep()` should be used instead of `time.sleeo()`, so that the single main thread is not blocked.
- A well designed coroutine should be nonblocking, i.e. releases GIL when possible.
- `Task` vs `Future`
- `Task` is a subclass of `Future`, `Future` is closer to `Promise` in JavaScript and is mostly used for framework.
- `Future` has methods such as `set_value()`, `done()`, `cancel()`
- `asyncio.Future` and `concurrent.futures.future` are mostly the same, but the latter is not `await`-able.
- `asyncio.get_event_loop` (globally) and `asyncio.get_running_loop` (inside a coroutine) to get the loop -- manually managing the loop is no longer needed.
- To cancel tasks, use `Task.cancel()`, where `CancelledError` will be raised at the `await` expression. In normal exit, `StopIteration` is raised.
- `await asyncio.sleep(0)` can be used as a gap to return control so that other threads can proceed. However CPU intensive operations should still be delegated to processes.
- For blocking functions:
- Run them in a (thread or process) executor with `loop.run_in_executor()`
- Or delegate to another thread with `asyncio.to_thread()`
- Objects cannot be used as sentinel since they lose identity after serialization and deserialization. `None` is not suitable if it can occur in the data stream.
- `futures.as_complete` forms a generator that `yield`s future result as they're done, which can be combined with `tqdm` to create progress bar.
- `futures.result()` returns a value or raises exceptions caught.
- Other `async` syntactic sugars
- Asynchronous context manager and generators, with `__aenter__`, `__aexit__`, and `__anext__`, `__aiter__`.
- Async generators.
- Async iterators, with `def __aiter__` and `async def __anext__`.
## Libraries
- `greenlet` can be used for cooperative multitasking without special syntax, it's used by `SQLAlchemy` internally. `gevent` makes Python's `socket` library nonblocking.
- `lelo` library allows parallelizing functions simply by `@parallelize` decorator
- Unlike [[js|JavaScript]], Python supports 3rd party async runtime such as `Curio` and `Trio`, which may provide more sensible API. (since `asyncio` allows other implementations of event loop)