asyncio in einer Schleife?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Hi, ich versuche gerade asyncio zu verstehen. Vielleicht geht das, was ich mir vorstelle, auch gar nicht. Jedenfalls habe ich dafür noch kein Beispiel gefunden.

Angenommen ich habe eine Programmschleife, die endlos läuft. Zu einem bestimmten Punkt möchte ich eine IO Anfrage ausführen. Die Schleife soll anschließend weiterlaufen und bei jedem Durchlauf fragen, ob die IO Anfrage ein Ergebnis geliefert hat. Um das mal in Pseudocode zu formulieren:

Code: Alles auswählen

while True:
	result = do_some_asyncio()
	if result:
		do_something(result)
	else
		do_something_else()
	do_even_more()
	and_more()
Ich weiß, dass es so nicht funktioniert, aber das ist in etwa die Idee. Ist das irgendwie möglich?
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@burli,

Es spricht nichts gegen so eine Schleife.
Hier ist eine Beispiel mit einem Websocket-Server:
https://websockets.readthedocs.io/en/st ... ed-example

Du musst aber die Funktionsaufrufe abwarten also ein "await" davor setzen.

Code: Alles auswählen

result = await do_some_asyncio()
Was ist mit den anderen Funktionen? Sind die nicht "async"? Könnte es passieren, dass sie den Ablauf blockieren?

Und wie du sicher weist, muss das alles in eine async-Funktion eingebaut werden. (So wie in dem Websocket-Beispiel)
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

Aber "await" blockiert doch die Ausführung und wartet, bis die Funktion fertig ist. Oder hab ich da was falsch verstanden? Damit habe ich aber wieder einen sequentielle Ablauf und kann auf asyncio verzichten.

Die Schleife soll aber weiterlaufen und bei jedem Durchlauf prüfen, ob die Funktion ein Ergebnis geliefert hat.
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@burli,

das ist in diesem Fall richtig. Da du das Ergebnis im nächsten Funktionsaufruf verwenden willst, must du es aber nun einmal abwarten.
async Funktionen müssen immer mit "await" aufgerufen werden, da es sonst eine Fehlermeldung geben würde. Das geht so also auch gar nicht anders.
Parallelität ist dennoch möglich. Mit asyncio.gather().

Ich habe hier zwei Beispiele, die das hoffentlich verdeutlichen:

Code: Alles auswählen

import asyncio
import time

async def aaaa():
    await asyncio.sleep(1)
    return "AAAA"

async def bbbb():
    await asyncio.sleep(1)
    return "BBBB"

async def main():
    while True:
        start = time.perf_counter()
        print(await aaaa())
        print(await bbbb())
        print(f"Dauer: {time.perf_counter()-start:.2f}s")

asyncio.run(main())

"""Ausgabe:
AAAA
BBBB
Dauer: 2.03s
AAAA
BBBB
Dauer: 2.01s
...
"""
Ein Durchlauf dauert 2 Sekunden, da beide Funktionen abgewartet werden.

Code: Alles auswählen

import asyncio
import time

async def aaaa():
    await asyncio.sleep(1)
    return "AAAA"

async def bbbb():
    await asyncio.sleep(0.3)
    return "BBBB"

async def main():
    while True:
        start = time.perf_counter()
        responses = await asyncio.gather(*[aaaa(), bbbb()])
        for response in responses:
            print(response)
        print(f"Dauer: {time.perf_counter()-start:.2f}s")

asyncio.run(main())

"""
Ausgabe:
AAAA
BBBB
Dauer: 1.01s
AAAA
BBBB
Dauer: 0.99s
...
"""
Jetzt dauert ein Durchlauf nur noch 1 Sekunde, weil beide async Funktionen "quasi parallel" laufen und die langsamere von beiden 1 Sekunde braucht.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das, was du da beschreibst, klingt nach einem Future. Damit kannst du eine Schleife bauen, die weiterläuft, während im Hintergrund ein Task an der Erfüllung des Futures arbeitet. Und mit done prüfen, ob es soweit ist. Da geht aber natürlich nur, wenn der restliche code drumrum auch async ist, und auf Dinge wartet, so dass der Hintergrund Task gescheduled werden kann.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

__deets__ hat geschrieben: Samstag 31. Juli 2021, 10:27 Das, was du da beschreibst, klingt nach einem Future. Damit kannst du eine Schleife bauen, die weiterläuft, während im Hintergrund ein Task an der Erfüllung des Futures arbeitet. Und mit done prüfen, ob es soweit ist. Da geht aber natürlich nur, wenn der restliche code drumrum auch async ist, und auf Dinge wartet, so dass der Hintergrund Task gescheduled werden kann.
Vermutlich ist das meine Lösung. Ich habe dazu aber auch noch kein sinnvolles Beispiel gefunden. Jedenfalls keins, das ich verstanden habe
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@burli,

wie wärs mit Tasks:

Code: Alles auswählen

import asyncio


async def do_some_asyncio():
    await asyncio.sleep(1)
    return "result"


async def main():
    task = asyncio.create_task(do_some_asyncio())

    while True:
        if task.done():
            print(f"Do something with {task.result()}")
            break
        else:
            print(f"Do something else")
        print("Do event more")
        print("and more")
        await asyncio.sleep(0.5)


asyncio.run(main())

"""
Ausgabe:

Do something else
Do event more
and more
Do something else
Do event more
and more
Do something with result
"""
Ich habe die sleeps so gesetzt dass es mir einigermaßen sinnvoll erschien. Hier wird also zweimal der "else" Zweig durchlaufen. Dann schließlich der "if" Zweig.
Ich habe auch im "if" Zweig ein "break" gesetzt, damit die Endlosschleife nicht weiter läuft.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

rogerb hat geschrieben: Samstag 31. Juli 2021, 17:59 wie wärs mit Tasks:
Das sieht gut aus. Das sollte die gewünschte Funktion haben. Danke für das Beispiel.
Das schwierigste beim Programmieren ist, sinnvolle Variablen- und Funktionsnamen zu finden :lol:
Antworten