Working with the half-synchronous/half-asynchronous pattern
One of the main anti-patterns we discussed in the previous chapter was the mix of synchronous calls into asynchronous code, but more often than not you will have CPU-intensive workloads mixed with I/O-intensive operations. In such cases, enabling both worlds to work together instead of colliding is an incredibly useful capability, which the half-sync/half-async pattern delivers by delegating tasks that might block the main thread to an external executor thread. The following code (available at Chapter 6/half_sync.py) shows a simple implementation:
import asyncio
import concurrent.futures
async def cpu_bound_task(x):
await asyncio.sleep(1)
return x * x
def run_cpu_bound_task(x):
return asyncio.run(cpu_bound_task(x))
async def main():
with concurrent.futures.ThreadPoolExecutor() as pool:
loop = asyncio.get_running_loop()
tasks = [
loop.run_in_executor(pool, run_cpu_bound_task...