What is the correct way to switch freely between asynchronous tasks?

2024/9/16 23:11:47

Suppose I have some tasks running asynchronously. They may be totally independent, but I still want to set points where the tasks will pause so they can run concurrently.

What is the correct way to run the tasks concurrently? I am currently using await asyncio.sleep(0), but I feel this is adding a lot of overhead.

import asyncioasync def do(name, amount):for i in range(amount):# Do some time-expensive workprint(f'{name}: has done {i}')await asyncio.sleep(0)return f'{name}: done'async def main():res = await asyncio.gather(do('Task1', 3), do('Task2', 2))print(*res, sep='\n')loop = asyncio.get_event_loop()loop.run_until_complete(main())

Output

Task1: has done 0
Task2: has done 0
Task1: has done 1
Task2: has done 1
Task1: has done 2
Task1: done
Task2: done

If we were using simple generators, an empty yield would pause the flow of a task without any overhead, but empty await are not valid.

What is the correct way to set such breakpoints without overhead?

Answer

As mentioned in the comments, normally asyncio coroutines suspend automatically on calls that would block or sleep in equivalent synchronous code. In your case the coroutine is CPU-bound, so awaiting blocking calls is not enough, it needs to occasionally relinquish control to the event loop to allow the rest of the system to run.

Explicit yields are not uncommon in cooperative multitasking, and using await asyncio.sleep(0) for that purpose will work as intended, it does carry a risk: sleep too often, and you're slowing down the computation by unnecessary switches; sleep too seldom, and you're hogging the event loop by spending too much time in a single coroutine.

The solution provided by asyncio is to offload CPU-bound code to a thread pool using run_in_executor. Awaiting it will automatically suspend the coroutine until the CPU-intensive task is done, without any intermediate polling. For example:

import asynciodef do(id, amount):for i in range(amount):# Do some time-expensive workprint(f'{id}: has done {i}')return f'{id}: done'async def main():loop = asyncio.get_event_loop()res = await asyncio.gather(loop.run_in_executor(None, do, 'Task1', 5),loop.run_in_executor(None, do, 'Task2', 3))print(*res, sep='\n')loop = asyncio.get_event_loop()
loop.run_until_complete(main())
https://en.xdnf.cn/q/72633.html

Related Q&A

How to write integers to port using PySerial

I am trying to write data to the first serial port, COM1, using PySerial.import serial ser = serial.Serial(0) print (ser.name) ser.baudrate = 56700 ser.write("abcdefg") ser.close()ought to wo…

Pandas sort columns by name

I have the following dataframe, where I would like to sort the columns according to the name. 1 | 13_1 | 13_10| 13_2 | 2 | 3 9 | 31 | 2 | 1 | 3 | 4I am trying to sort the columns in the f…

Series objects are mutable, thus they cannot be hashed error calling to_csv

I have a large Dataframe (5 days with one value per second, several columns) of which Id like to save 2 columns in a csv file with python pandas df.to_csv module.I tried different ways but always get t…

Python client / server question

Im working on a bit of a project in python. I have a client and a server. The server listens for connections and once a connection is received it waits for input from the client. The idea is that the c…

Segmentation fault during import cv on Mac OS

Trying to compile opencv on my Mac from source. I have following CMakeCache.txt: http://pastebin.com/KqPHjBx0I make ccmake .., press c, then g. Than I make sudo make -j8: http://pastebin.com/cJyr1cEdTh…

Python bug - or my stupidity - EOL while scanning string literal

I cannot see a significant difference between the two following lines. Yet the first parses, and the latter, does not.In [5]: n=""" \\"Axis of Awesome\\" """In […

IOPub Error on Google Colaboratory in Jupyter Notebook

I understand that the below command jupyter notebook --NotebookApp.iopub_data_rate_limit=1.0e10 would let me set the data rate. But on Colab, I cannot run this command since the notebook is already ope…

Python code calls C library that create OS threads, which eventually call Python callbacks

If the one and only Python interpreter is in the middle of executing a bytecode when the OS dispatches another thread, which calls a Python callback - what happens? Am I right to be concerned about th…

Django MTMField: limit_choices_to = other_ForeignKeyField_on_same_model?

Ive got a couple django models that look like this:from django.contrib.sites.models import Siteclass Photo(models.Model):title = models.CharField(max_length=100)site = models.ForeignKey(Site)file = mod…

Logging django.request to file instead of console

I am trying to configure my django settings.py to properly use the python logging facility but Ive stumbled upon a rather strange problem:Even after reading the docs, I simply cant find out how to redi…