Pythonic Dev
678 subscribers
103 photos
1 video
25 links
Happy Coding πŸ’«
ADMIN: @cmatrix1
Download Telegram
πŸ“˜ Chapter 1: Getting to Know Asyncio

πŸ”„ What is asyncio?
asyncio is a Python library introduced in version 3.4 that allows you to run I/O-bound tasks concurrently. Rather than making your code wait for slow operations, asyncio allows multiple tasks to run "in parallel" by pausing tasks when they’re waiting for I/O. This allows Python to work on other tasks in the meantime.

πŸ–₯️ I/O-bound vs. CPU-bound Tasks
- I/O-bound: Tasks that wait for input/output, such as web requests or database queries. These tasks benefit greatly from asyncio as it allows them to pause and let other tasks run while waiting.
- CPU-bound: Tasks that use lots of computational power (like math calculations). asyncio isn't designed for CPU-heavy tasks.

βš™οΈ Concurrency, Parallelism, and Multitasking
- Concurrency: When multiple tasks appear to be running at the same time by taking turns. For example, while one task is waiting for a file to download, another task can start.
- Parallelism: When tasks are literally running at the same time on multiple CPU cores.
- Multitasking: Managing several tasks at once. This can be preemptive (the OS decides when to switch between tasks) or cooperative (tasks decide when to yield control). asyncio uses cooperative multitasking, meaning tasks "cooperate" by pausing when they reach I/O.

πŸ”„ Processes vs. Threads
- Processes: Independent units that do not share memory. They are good for CPU-bound tasks but use more resources.
- Threads: Lighter-weight than processes and share memory within the same program. However, Python’s Global Interpreter Lock (GIL) prevents threads from running Python code at the same time (no parallel execution). Still, threads are useful for I/O-bound tasks.

πŸ” Global Interpreter Lock (GIL)
- GIL is a mechanism in Python that allows only one thread to execute Python bytecode at a time, even on multi-core systems.
- This makes multithreading less useful for CPU-bound operations but still beneficial for I/O-bound tasks because I/O releases the GIL.

⚑ Single-threaded Concurrency
asyncio achieves concurrency without needing multiple threads by using non-blocking I/O and an event loop.

πŸ“¬ What is a Socket?
A socket is a low-level connection to send and receive data over a network. By default, sockets are blocking, meaning they make the program wait while they communicate. Non-blocking sockets allow the program to continue executing other tasks while waiting for a response. This is key to how asyncio achieves concurrency with just one thread.

πŸ”„ Event Loop
- An event loop is a core part of how asyncio works. It’s a loop that runs tasks, pausing and resuming them as necessary. The event loop checks if tasks are waiting on I/O and either runs them or pauses them until they’re ready.
- Tasks that involve I/O can pause and free up the loop to run other tasks, making programs more efficient.

πŸ•ΉοΈ How It Works:
1. Tasks are submitted to the event loop.
2. The loop starts running tasks. If a task hits an I/O operation (like a web request), the task pauses.
3. The operating system watches the socket and informs the event loop when the I/O is complete.
4. The event loop resumes the paused task and continues processing.

πŸ€” Why Use asyncio?
- Improves performance for programs that do a lot of waiting (web servers, file reading).
- Lightweight and doesn’t need multiple threads or processes to handle concurrency.
- Efficient resource utilization: While waiting for slow I/O operations, the CPU can continue working on other tasks, leading to faster overall execution.

πŸš€ Key Takeaways:
- asyncio is ideal for I/O-bound tasks and allows you to write concurrent programs using a single thread.
- It does not remove Python's GIL but makes it less of an issue by focusing on I/O tasks.
- Non-blocking I/O and the event loop allow tasks to pause and resume, making concurrency efficient even with just one thread.

#PythonConcurrencyWithAsyncio
#Chapter_01
#Notes #Book
❀4πŸ‘1
πŸ“˜ Chapter 2: *Asyncio Basics* πŸš€

Chapter 2 delves into the foundational aspects of asyncio in Python, focusing on how it enables single-threaded concurrency using coroutines, tasks, and event loops. Here’s a breakdown:


πŸŒ€ 2.1 Introducing Coroutines
- Coroutines are special Python functions that can pause and resume execution when encountering a potentially long-running task.
- When a coroutine pauses to wait for an operation, other tasks can run concurrently, providing concurrency. πŸ’‘
- `async` and `await` are the two essential keywords:
- async defines a function as a coroutine.
- await pauses the coroutine until a result is available from an asynchronous operation.

βš™οΈ Example of Creating a Coroutine
async def my_coroutine() -> None:
print("Hello world!")

This is similar to a normal Python function but can pause its execution.


⏳ 2.2 Introducing Long-Running Coroutines with `sleep`
- Asyncio’s sleep function allows us to pause execution, simulating real-world, long-running operations like web requests or database queries 🌐.
- When await asyncio.sleep() is called, other tasks can be executed during the pause.

βš™οΈ Example of Using asyncio.sleep
async def hello_world_message() -> str:
await asyncio.sleep(1)
return "Hello World!"

This coroutine pauses for 1 second before returning "Hello World!". During that second, other coroutines can run concurrently.


πŸ”„ 2.3 Running Concurrently with Tasks
- A task is a wrapper around a coroutine that schedules it to run on the event loop πŸ•‘.
- Tasks allow coroutines to be run concurrently, as they don’t block the event loop, unlike await, which pauses until a result is returned.

βš™οΈ Example of Creating a Task
import asyncio
from util import delay

async def main():
task = asyncio.create_task(delay(3))
await task

Here, the task delay(3) runs concurrently, while other code can execute.


β›” 2.4 Canceling Tasks and Setting Timeouts
- Tasks can be canceled using task.cancel(), raising a CancelledError within the task. If a task is taking too long, we can also set timeouts using asyncio.wait_for πŸ•’.

βš™οΈ Example of Cancelling a Task
async def cancel_task(task):
task.cancel()
try:
await task
except asyncio.CancelledError:
print("Task was cancelled")



πŸ’Ό 2.5 Tasks, Coroutines, Futures, and Awaitables
- Futures represent a value that will be available in the future but might not exist yet. They are used internally in asyncio and can be awaited 🎯.
- Coroutines and tasks can both be used in await expressions.

βš™οΈ Example of Working with Futures
from asyncio import Future

my_future = Future()
my_future.set_result(42)
print(my_future.result()) # Outputs: 42



⏱️ 2.6 Measuring Coroutine Execution Time with Decorators
- By using decorators, we can measure the execution time of coroutines for performance analysis. βŒ›


⚠️ 2.7 Pitfalls of Coroutines and Tasks
- Be cautious with CPU-bound code inside coroutines, as it will block the event loop.
- Avoid using blocking I/O APIs; use asyncio-compatible libraries to ensure the event loop runs smoothly πŸ› οΈ.


πŸ”§ 2.8 Accessing and Manually Managing the Event Loop
- You can access the event loop directly using asyncio.get_event_loop(), though asyncio.run() is recommended for running main coroutines.


πŸ› οΈ 2.9 Using Debug Mode
- Debug mode helps in identifying long-running coroutines or tasks that block the event loop 🧐. You can enable it with:
python3 -X dev program.py

Or by setting the PYTHONASYNCIODEBUG environment variable.

βš™οΈ Example of Running in Debug Mode
asyncio.run(main(), debug=True)


#PythonConcurrencyWithAsyncio
#Chapter_02
#Notes #Book
❀3πŸ‘2