Alright, how to run blocking tasks with asyncio because who doesn’t love a good paradox?
First: what is a “blocking task”? It’s any operation that takes control of the CPU and prevents other code from running until it finishes. This can be anything from reading data from a file to performing complex calculations or waiting for user input. In traditional programming, you would run these tasks synchronously one at a time which can lead to slow performance and unresponsive applications.
But with asyncio (short for “asynchronous I/O”), we can handle multiple tasks simultaneously without blocking the event loop! This is great news for CPU-bound operations that don’t involve waiting on external resources, but what about those ***** blocking tasks? Can they be run in an asynchronous way too?
The answer is yes… sort of. We can use a technique called “threading” to spin off a new thread and execute the blocking task there while our main event loop continues processing other tasks. This allows us to take advantage of asyncio’s concurrency benefits without sacrificing performance for CPU-bound operations.
Here’s an example script that demonstrates how to run a simple blocking function asynchronously using asyncio:
# This script demonstrates how to run a simple blocking function asynchronously using asyncio.
# Import necessary libraries
import time
import threading
from typing import List, Callable
import asyncio
# Define a function that will run the given function in a separate thread and return when it's done.
def blocking_function(func: Callable) -> None:
t = threading.Thread(target=func) # Create a new thread with the given function as the target
t.daemon = True # Set daemon to True so that the main program exits when this thread is finished.
t.start() # Start the thread
t.join() # Wait for the thread to finish before continuing with the main program
# Define our main function
async def main():
print("Started at:", time.time()) # Print the current time
await asyncio.sleep(1) # Asynchronously wait for 1 second
blocking_function(some_blocking_func) # Call our blocking function asynchronously!
print("Finished at:", time.time()) # Print the current time
# Check if the script is being run directly
if __name__ == "__main__":
loop = asyncio.get_event_loop() # Get the event loop
loop.run_until_complete(asyncio.gather(*sys.argv[1:])) # Run our main function asynchronously!
# The above line runs the main function asynchronously and passes any command line arguments as parameters to the function.
# The asterisk (*) unpacks the arguments into individual parameters.
# This allows us to run multiple functions asynchronously at the same time.
# Note: The following function is not defined in the script, but it is referenced in the main function.
# It is assumed that it is defined elsewhere in the code.
def some_blocking_func():
# This function represents a blocking operation that would normally slow down the main program.
# By running it asynchronously, we can continue processing other tasks while this function is running.
# This allows us to take advantage of asyncio's concurrency benefits without sacrificing performance for CPU-bound operations.
time.sleep(2) # Simulate a 2 second delay
print("Blocking function finished at:", time.time()) # Print the current time when the function finishes.
In this example, we define a `blocking_function` that takes a callable (i.e., a function) and runs it in a separate thread using the built-in `threading` module. We set the daemon flag to True so that the main program exits when this thread is finished.
In our `main` function, we use asyncio’s `asyncio.sleep()` method to simulate some blocking I/O and then call our `blocking_function` asynchronously using the `await` keyword. This allows us to take advantage of asyncio’s concurrency benefits while still running CPU-bound operations in a separate thread.
To run this script, save it as `asyncio_blocking.py`, and then execute:
#!/bin/bash
# This script is used to run a blocking function asynchronously using the asyncio library in Python.
# First, we import the necessary libraries.
import asyncio
import sys
# Next, we define our blocking function.
def blocking_function():
# This function performs some blocking I/O operations.
# It is important to note that these operations will block the main thread, preventing other code from running until they are completed.
# This is why we will be using asyncio to run this function asynchronously.
# By using the await keyword, we can take advantage of asyncio's concurrency benefits while still running CPU-bound operations in a separate thread.
# This allows our code to continue running while the blocking function is being executed.
# This is especially useful for long-running operations that would otherwise slow down our program.
# In this case, we are simply printing a message, but in a real-world scenario, this function could be performing more complex tasks.
print("Running blocking function...")
# Now, we define our main function.
async def main():
# This function will be responsible for calling our blocking function asynchronously.
# First, we use the asyncio library to create a new event loop.
loop = asyncio.get_event_loop()
# Next, we use the loop's run_in_executor method to run our blocking function in a separate thread.
# This ensures that our main thread is not blocked while the function is being executed.
# We pass in our blocking function as the first argument, and any additional arguments as subsequent arguments.
# In this case, we are passing in the name of the file containing our blocking function as an argument.
await loop.run_in_executor(None, blocking_function, sys.argv[1])
# Finally, we close the event loop.
loop.close()
# Now, we call our main function.
# We use the asyncio library's run method to run our main function asynchronously.
# This ensures that our program does not exit before our main function has finished executing.
asyncio.run(main())
# To run this script, save it as asyncio_blocking.py, and then execute:
# python3 asyncio_blocking.py some_blocking_func.py
# This will run our blocking function asynchronously, allowing our code to continue running while the function is being executed.
Replace `some_blocking_func.py` with the path to your actual blocking function (which should be a separate Python script). This will run both scripts asynchronously using asyncio!