R

ReflexKR

Reflex-Dokumentation

Nebenläufigkeit in der Nachtküche

·

Kursüberblick

1

Die Schlange am ersten Abend: Nebenläufigkeit und Parallelität

2

Mehr Köche einstellen: Grundlagen von threading

3

Die Zahl auf der Tafel stimmt nicht: Race Conditions und Lock

4

Nur drei Herdplatten: Synchronisationswerkzeuge

5

Messer und Bretter: Deadlock

6

Die Bonschiene: queue

7

Leihköche: concurrent.futures

8

Warum der Teig nicht schneller wird: der GIL und Parallelität

9

Eine Servicechefin für alle Tische: erste Schritte mit asyncio

10

Die Gruppenreservierung platzt: strukturierte Nebenläufigkeit

11

Der Gastraum steht still: blockierende Aufrufe und Kombinieren

12

Die Nacht der Kritikerin: alles zusammen

Nebenläufigkeit in der Nachtküche
Lektion 12

Zeit 9:10–10:00

Die Nacht der Kritikerin: alles zusammen

Der Bon für heute Nacht

#12 · Zwölfte Nacht

Die Nacht der Kritikerin

Problem

Die Frau am Fenster legt ihre Visitenkarte auf den Tisch: Gastrokritikerin. Heute Nacht bestellt sie ganz offiziell ein Menü mit zwölf Gängen. Der 3., 7. und 11. Teller brauchen beim ersten Versuch auffällig lange.

Ziel

Alle zwölf Teller rausschicken, dabei höchstens drei Herdplatten benutzen und in unter 3 Sekunden fertig sein.

Nachtlokal Dongsi

Freitagnacht, zehn Uhr. Wie versprochen kommt die Frau am Fenster herein. Immer allein, immer eine Schüssel Nudelsuppe, immer Armbanduhr und kleines Notizbuch. Heute legt sie, kaum dass sie sitzt, eine Visitenkarte auf den Tisch. Gastrokritikerin.

„Ich habe diese Küche in den letzten Wochen beobachtet. Heute bestelle ich richtig. Das Menü, zwölf Gänge.“ Die Kritikerin schlägt ihr Notizbuch auf und nimmt die Armbanduhr ab.

In der Küche wird es still. Minho lässt sein Messer fallen, Sua sortiert die Bonschiene noch einmal neu, und Herr Tak wischt einfach seine Herdplatte ab. Lou kommt mit dem Bon herein. Chefin Yoon Seoyeon dreht den Timer an ihrem Hals auf null. „Eine Küche ist ein Programm. Heute lassen wir alles laufen, was wir gebaut haben.“

12-1. Welches Werkzeug nehmen wir? (8 Min.)

In elf Nächten haben sich viele Werkzeuge angesammelt. Kommt ein Gast herein, stellst du zuerst eine einzige Frage: „Ist das Arbeit mit Warten oder Arbeit mit vollen Händen?“

SituationWerkzeugIn der KücheLektion
Sehr viel Warten (Hunderte bis Zehntausende Verbindungen)asyncio (gather, TaskGroup)Lou, die allein den Gastraum betreut9–10
Warten, aber nur synchrone Bibliotheken, ein paar Dutzend Aufgabenthreading, ThreadPoolExecutormehrere Köche, Leihköche2, 7
Viel Rechnen (CPU)ProcessPoolExecutor, multiprocessing (oder Threads unter 3.14t)Filialküche8
Alter blockierender Code innerhalb von asyncioasyncio.to_threadeine eigene Person vor dem Ofen11
Schwere Berechnung innerhalb von asyncioloop.run_in_executor(Prozess-Pool, ...)den Teig an die Filiale geben11
Mehrere ändern denselben WertLock, oder per Queue einen einzigen Besitzer bestimmenLock an der Tafel, Bonschiene3, 6
Höchstens n gleichzeitigSemaphore(n)drei Herdplatten4, 11
Muss zusammen enden und darf die Zeit nicht überschreitenTaskGroup, asyncio.timeoutGruppenreservierung10

In dieser Reihenfolge wählst du aus.

  1. Brauche ich überhaupt Nebenläufigkeit? Ist es der Reihe nach schnell genug, lass es so. Wie in Lektion 1 gesehen: Nebenläufigkeit gibt es nicht umsonst.
  2. Warten oder Rechnen? Bei Warten asyncio oder Threads, bei Rechnen Prozesse (oder 3.14t). In einem Build mit GIL wird Rechenarbeit nicht schneller, wenn du sie auf Threads verteilst (Lektion 8).
  3. Unterstützt die Bibliothek, die du schon benutzt, async? Wenn ja, nimm asyncio; wenn nicht, fang mit Threads an oder verpack sie innerhalb von asyncio mit to_thread.
  4. Was wird gemeinsam benutzt? Bei gemeinsamem Zustand kommt ein Lock oder eine Queue dazu, bei begrenzten Ressourcen eine Semaphore, und wenn alles zusammen enden muss, ein TaskGroup.

12-2. Der Menü-Prozessor für die Kritikerin (14 Min.)

Der Bon der Kritikerin sieht so aus: zwölf Teller, 0,5 Sekunden pro Teller. Aber der 3., 7. und 11. Teller dauern beim ersten Versuch dreimal so lange (1,5 Sekunden), weil die Zutaten noch nicht ganz aufgetaut sind. Beim zweiten Versuch klappt es in 0,5 Sekunden. Herdplatten gibt es nur drei.

Wir brauchen drei Bauteile: eine Semaphore, die die Zahl der Herdplatten einhält, ein Zeitlimit, damit sich kein Teller in die Länge zieht, und Wiederholungen für abgebrochene Teller. Das alles packen wir in einen TaskGroup.

import asyncio, time

SLOW = {3, 7, 11}                      # Teller, die beim ersten Mal dreimal so lange brauchen

async def plate(n, attempt):
    await asyncio.sleep(0.5 * (3 if n in SLOW and attempt == 0 else 1))

async def order(n, stoves, limit_s, retries):
    for attempt in range(retries + 1):
        async with stoves:             # gekocht wird nur, solange man eine Herdplatte hat
            try:
                async with asyncio.timeout(limit_s):
                    await plate(n, attempt)
                return True
            except TimeoutError:
                pass                   # Platte abgeben, hinten anstellen, noch mal
    return False

async def service(stoves_n, limit_s, retries):
    stoves = asyncio.Semaphore(stoves_n)
    start = time.perf_counter()
    async with asyncio.TaskGroup() as tg:
        tasks = [tg.create_task(order(n, stoves, limit_s, retries)) for n in range(1, 13)]
    served = sum(t.result() for t in tasks)
    print(f"stoves={stoves_n} limit={limit_s} retries={retries}: "
          f"served {served}/12, {time.perf_counter() - start:.1f}s")

asyncio.run(service(3, None, 0))       # ohne Zeitlimit
asyncio.run(service(3, 0.6, 0))        # nur Zeitlimit
asyncio.run(service(3, 0.6, 1))        # Zeitlimit + eine Wiederholung
# stoves=3 limit=None retries=0: served 12/12, 3.5s
# stoves=3 limit=0.6 retries=0: served 9/12, 2.2s
# stoves=3 limit=0.6 retries=1: served 12/12, 2.7s
# (die Laufzeit schwankt von Lauf zu Lauf um etwa 0,1 s; die erste Zeile zeigt 3.5s oder 3.6s)
  • asyncio.timeout(None) heißt: kein Limit. Deshalb kann dieselbe Funktion sowohl „ohne Limit“ als auch „mit 0,6 Sekunden Limit“ laufen.
  • Ohne Limit halten die drei langsamen Teller ihre Herdplatte je 1,5 Sekunden fest, und bis alle zwölf Teller draußen sind, dauert es gut 3,5 Sekunden.
  • Mit Limit allein werden die langsamen Teller nach 0,6 Sekunden abgeschnitten, und die Platten werden schnell frei. Dafür gehen diese drei Teller nie raus.
  • Mit Limit und Wiederholung legt ein abgeschnittener Teller seine Platte ab und stellt sich hinten wieder an. Beim zweiten Mal ist er nach 0,5 Sekunden fertig, und alle zwölf Teller gehen raus.

Am Design sind zwei Punkte wichtig.

  • Beim Wiederholen wird die Herdplatte abgegeben. async with stoves steht innerhalb des for, also gibt ein abgeschnittener Teller die Platte frei und stellt sich neu an. Würde er mit der Platte in der Hand wiederholen, müssten andere Teller so lange untätig warten.
  • Ein Fehlschlag wird nicht als Exception geworfen, sondern als False zurückgegeben. Fliegt in einem TaskGroup eine Exception, wird alles andere abgebrochen (Lektion 10). Bei einer Gruppenreservierung ist das richtig, aber bei einem Menü sollen die übrigen Teller auch dann rausgehen, wenn einer scheitert. Aus demselben Grund könnte man die Aufgaben auch mit asyncio.gather sammeln.

In der Praxis wartet man zwischen den Wiederholungen oft etwas länger, etwa mit await asyncio.sleep(0.05 * 2 ** attempt). So verhindert man, dass alle gleichzeitig wieder herandrängen (aus demselben Grund wie beim Livelock in Lektion 5).

Die Zeitleiste unten lässt denselben Prozessor auf dem Server laufen. Eine Zeile ist ein Teller; Versuche, die vom Zeitlimit abgeschnitten wurden, sind grau, Wiederholungen tragen r1, r2. Die Starteinstellung verfehlt das Ziel mit Absicht.

Küchen-Zeitleiste

Bestellungen

6

Herdplatten

2

Zeitlimit (s)

aus

Wiederholungen

0

Kochen

Warten

Abgebrochen

Fehlgeschlagen

Die zwölf Teller der Kritikerin. Ändere Herdplatten, Zeitlimit und Wiederholungen so, dass du „Serviert 12 · Fehlgeschlagen 0, höchstens 3 Herdplatten, unter 3 Sekunden“ erreichst. Mit der Starteinstellung geht kein einziger Teller raus.
  • Mit der Starteinstellung (2 Herdplatten, Zeitlimit 0,4 Sekunden, 0 Wiederholungen) kommt nach etwa 2,4 Sekunden Serviert 0 · Fehlgeschlagen 12 heraus. Schon ein normaler Teller braucht 0,5 Sekunden, das Limit liegt aber bei 0,4, also werden alle Teller abgeschnitten.
  • Erhöhst du auf 3 Herdplatten und stellst nur die Wiederholungen auf 2, wird es schlimmer. Mit demselben Limit von 0,4 Sekunden wird jeder Teller eben dreimal abgeschnitten, es bleibt bei Serviert 0, nur die Zeit steigt auf etwa 4,9 Sekunden. Wiederholungen reparieren kein falsch gesetztes Zeitlimit. Selbst wenn du das Limit auf genau 0,5 Sekunden setzt, so lang wie ein normaler Teller, wird jeder knapp abgeschnitten, und alle scheitern.
  • Schaltest du das Zeitlimit aus, wird alles serviert, aber selbst mit 3 Herdplatten dauert es gut 3,5 Sekunden. Mit 4 Herdplatten sind es etwa 3,0 Sekunden, also haarscharf, und vor allem haben wir nur drei Platten.
  • Die Anzeige wartet nur etwa 5 Sekunden auf das Experiment. Langsame Einstellungen wie 2 Herdplatten mit 2 Wiederholungen können deshalb mittendrin abgeschnitten aussehen. Ändere dann die Einstellung und lass es noch einmal laufen.

Tipp der Chefin

Ein Zeitlimit heißt: ‚länger als ein normaler Teller, kürzer als ein verpatzter‘. Schneidest du schon die normalen Teller ab, helfen dir auch noch so viele Wiederholungen nichts. Bevor du eine Zahl festlegst, miss erst, wie lange es normalerweise dauert.

12-3. Alles in einer Küche (8 Min.)

Im Menü der Kritikerin kommt alles zusammen: Teig (Rechnen), Brot aus dem alten Ofen (blockierend) und Gerichte vom Herd (Warten). Jedes geht an die passende Stelle, und Lou im Gastraum wartet auf alles. Weil Prozesse im Spiel sind, speicherst du den Code als Datei und führst ihn aus.

import asyncio, threading, time
from concurrent.futures import ProcessPoolExecutor

def knead(n):                          # Teig: Arbeit mit vollen Händen → Filialküche (Prozess)
    return sum(i * i for i in range(n)) % 97

tally_lock = threading.Lock()          # das Buch, in das Ofen-Thread und Loop beide schreiben
tally = {"plates": 0}

def count_plate():
    with tally_lock:                   # im Lock kein await und nichts Langes
        tally["plates"] += 1

def old_oven(item):                    # der alte Ofen: blockierend → Thread
    time.sleep(0.4)
    count_plate()
    return f"{item} baked"

async def stove_dish(n, stoves):       # Gerichte vom Herd: Semaphore
    async with stoves:
        await asyncio.sleep(0.2)
    count_plate()
    return f"#{n}"

async def main():
    loop = asyncio.get_running_loop()
    stoves = asyncio.Semaphore(3)
    with ProcessPoolExecutor(max_workers=2) as dough_room:
        async with asyncio.timeout(5):         # Deadline für das ganze Menü
            results = await asyncio.gather(
                loop.run_in_executor(dough_room, knead, 3_000_000),
                asyncio.to_thread(old_oven, "bread"),
                *(stove_dish(n, stoves) for n in range(1, 7)),
            )
    print(results)
    print(tally)

if __name__ == "__main__":
    asyncio.run(main())
# [56, 'bread baked', '#1', '#2', '#3', '#4', '#5', '#6']
# {'plates': 7}
ArbeitWer sie übernimmtWerkzeug
Der ganze Gastraum, das WartenLouasyncio.run, gather
Teig (Rechnen)Filialkücherun_in_executor + ProcessPoolExecutor
Alter Ofen (blockierend)ein Koch vor dem Ofenasyncio.to_thread
Gerichte vom Herddrei Herdplattenasyncio.Semaphore(3)
Das Buch (gemeinsamer Zustand)Lock an der Tafelthreading.Lock (weil Thread und Loop es gemeinsam benutzen)
Deadlineder Timer der Chefinasyncio.timeout(5)

Weil sowohl der Ofen-Thread als auch die Loop ins Buch schreiben, haben wir threading.Lock verwendet. Dass die Loop diesen Lock greifen darf, liegt daran, dass im Lock kein await steht: Es wird nur eine Zahl geändert und sofort wieder freigegeben. Der Unfall aus Lektion 11 passiert, wenn man mit dem Lock in der Hand per await den Platz freigibt.

12-4. Checkliste für Nebenläufigkeitsbugs (6 Min.)

Damit die Küche vor der Kritikerin nicht stehen bleibt, prüfen wir vor dem Service diese sechs Punkte.

PrüfpunktSymptomGegenmittelLektion
Gemeinsamer ZustandZahlen stimmen manchmal nichtLesen bis Schreiben mit Lock zusammenfassen oder per Queue einen einzigen Besitzer bestimmen3, 11
Lock-ReihenfolgeHängt manchmal für immerAlle sperren in derselben Reihenfolge, acquire(timeout=)5
Blockierende Aufrufeasyncio ist insgesamt langsamto_thread, mit debug=True aufspüren11
Verschluckter AbbruchZeitlimit und TaskGroup greifen nichtCancelledError nach dem Aufräumen wieder mit raise werfen10
Tasks ohne ReferenzHintergrundarbeit verschwindet manchmalTasks sammeln und festhalten oder TaskGroup9
Queue, die nie fertig wirdjoin() kehrt nicht zurückso oft task_done() wie herausgenommen, Endsignal (None, shutdown())6, 11

Das Gegenmittel für „Tasks ohne Referenz“ sieht so aus: Tasks in einer Menge festhalten und sie sich selbst austragen lassen, wenn sie fertig sind.

import asyncio

running = set()                        # hier werden die Tasks festgehalten

async def wipe(table):
    await asyncio.sleep(0.05)
    print("wiped", table)

async def main():
    for t in ("T1", "T2"):
        task = asyncio.create_task(wipe(t))
        running.add(task)
        task.add_done_callback(running.discard)   # trägt sich selbst aus, wenn fertig
    await asyncio.sleep(0.1)
    print(len(running))

asyncio.run(main())
# wiped T1
# wiped T2
# 0

Eine „Queue, die nie fertig wird“ entsteht, sobald auch nur ein einziges task_done() fehlt. Hast du den Verdacht, setz ein Zeitlimit auf join() und prüfe.

import asyncio

async def cook(q):
    while True:
        await q.get()
        await asyncio.sleep(0.01)
        # q.task_done()  ← vergessen

async def main():
    q = asyncio.Queue()
    for n in range(3):
        q.put_nowait(n)
    worker = asyncio.create_task(cook(q))
    try:
        async with asyncio.timeout(0.3):
            await q.join()
    except TimeoutError:
        print("join never finished:", q.qsize(), "in queue")
    worker.cancel()

asyncio.run(main())
# join never finished: 0 in queue

Die Queue ist leer (0 in queue), aber join() wird nicht fertig. Die drei herausgenommenen Bons haben nie das Zeichen „erledigt“ bekommen.

12-5. Die Küchen der elf Nächte noch einmal laufen lassen (4 Min.)

Wir haben die Küchen der vergangenen Nächte in einem Versuchsaufbau gesammelt. Lass dieselben acht Bestellungen laufen und wechsle nur die Arbeitsweise. Die Regler für Köche und Herdplatten wirken nur in den Modi, die diese Werte verwenden.

Küchen-Zeitleiste

Bestellungen

6

Köche

3

Herdplatten

2

Kochen

Warten

Abgebrochen

Fehlgeschlagen

8 Bestellungen, je 0,3 Sekunden. Lass nacheinander „Allein, eins nach dem anderen“, „Mehrere Köche“, „Begrenzte Herdplatten“, „Leihköche“ und „Alles auf einmal mit gather“ laufen und vergleich die Form der Balken und die Dauer.
  • Allein, eins nach dem anderen: eine Zeile, etwa 2,4 Sekunden (Lektion 1).
  • 4 Köche: vier Zeilen überlappen, etwa 0,6 Sekunden (Lektion 2). Begrenzt du auf 2 Herdplatten, entsteht gelbes Warten, etwa 1,2 Sekunden (Lektion 4).
  • Leihköche: Je höher die Nummer, desto kürzer die Bestellung, deshalb ist die Rückgabe-Reihenfolge durcheinander; etwa 0,4 Sekunden (Lektion 7).
  • gather: Alle acht Tische überlappen, etwa 0,3 Sekunden (Lektion 9). Unabhängig von der Zahl der Köche, Lou ganz allein.

Küchenschluss (10 Min.)

Die Einstellung, mit der das Menü der Kritikerin klappt, ist 3 Herdplatten, Zeitlimit 0,6 Sekunden, 1 Wiederholung. Diese Werte haben wir mit der Engine des Versuchsaufbaus auf dem Server selbst durchlaufen lassen und geprüft.

HerdplattenZeitlimitWiederholungenServiert · FehlgeschlagenDauerZiel
20,4 s00 · 12etwa 2,4 sgescheitert
3aus012 · 0etwa 3,5–3,6 szu langsam
4aus012 · 0etwa 3,0 szu viele Herdplatten
30,4 s20 · 12etwa 4,9 sgescheitert
30,6 s09 · 3etwa 2,2 s3 Teller gescheitert
30,6 s112 · 0etwa 2,7 sgeschafft
# mit der Funktion service aus 12-2
asyncio.run(service(3, 0.6, 1))
# stoves=3 limit=0.6 retries=1: served 12/12, 2.7s
  • Ein Limit von 0,6 Sekunden lässt normale Teller (0,5 Sekunden) durch und schneidet nur die beim ersten Mal verschleppten Teller (1,5 Sekunden) nach 0,6 Sekunden ab.
  • Die drei abgeschnittenen Teller geben ihre Platte ab, versuchen es hinten in der Schlange noch einmal und sind nach 0,5 Sekunden fertig.
  • Auch mit einem etwas knapperen Limit von 0,55 Sekunden wird das Ziel erreicht (etwa 2,65 Sekunden). Mit 0,7 Sekunden sind es etwa 2,9 Sekunden, ganz dicht an 3 Sekunden, und mit 0,8 Sekunden geht es über 3 Sekunden.

Der zwölfte Teller geht raus, 2,7 Sekunden. Der Timer der Chefin bleibt stehen. Die Kritikerin schreibt ein paar Zeilen in ihr Notizbuch, leert ihre Nudelschüssel und steht auf. An der Kasse reißt sie eine Seite aus dem Notizbuch und reicht sie der Chefin.

„Zwölf Teller, und kein einziger kam kalt an. Gewartet wurde schon, aber niemand stand untätig herum. Ich komme wieder. Nächstes Mal als ganz normaler Gast.“

Die Kritikerin geht, und in der Küche gehen die Lichter eins nach dem anderen aus. Die Chefin zieht die letzte Rezeptkarte aus ihrer Schürzentasche und reicht sie dir. „Das waren zwölf Nächte. Morgen um zehn ist die Tür auch wieder offen. Diesmal bist du dran, den Bon zu schreiben. Was kommt in die erste Zeile?“

Zwölf Nächte im Rückblick

LektionDie NachtWas hängen bleibt
1Die Schlange am ersten Abend: Nebenläufigkeit und ParallelitätArbeit mit Warten und Arbeit mit vollen Händen auseinanderhalten. Nebenläufigkeit heißt abwechselnd, Parallelität heißt im selben Moment.
2Mehr Köche einstellen: Grundlagen von threadingThread(target=), start(), join(). Ergebnisse sammelst du selbst ein.
3Die Zahl auf der Tafel stimmt nicht: Race Conditions und LockDrängt sich jemand zwischen Lesen und Schreiben, stimmt die Zahl nicht. with lock: kurz halten.
4Nur drei Herdplatten: SynchronisationswerkzeugeSemaphore, Event, Condition, Barrier.
5Messer und Bretter: DeadlockIn derselben Reihenfolge sperren, oder ablegen, wenn es hakt.
6Die Bonschiene: queueErzeuger und Verbraucher per Queue trennen, mit task_done() und join() wissen, wann Schluss ist.
7Leihköche: concurrent.futuresThreadPoolExecutor, Future, as_completed und map.
8Warum der Teig nicht schneller wird: der GIL und ParallelitätRechnen in Prozesse auslagern, oder 3.14t nehmen.
9Eine Servicechefin für alle Tische: erste Schritte mit asyncioasync def, await, gather, create_task.
10Die Gruppenreservierung platzt: strukturierte NebenläufigkeitTaskGroup, except*, asyncio.timeout; Abbrüche wieder werfen.
11Der Gastraum steht still: blockierende Aufrufe und KombinierenBlockierendes per to_thread, asyncio-Werkzeuge nur in asyncio.

Schichtbuch — Zwölfte Nacht. Alle zwölf Teller der Kritikerin mit 3 Herdplatten, 0,6 Sekunden Zeitlimit und 1 Wiederholung in etwa 2,7 Sekunden rausgeschickt. Gastraum mit asyncio, Teig in Prozessen, alter Ofen im Thread, Herdplatten per Semaphore. Ab morgen schreibst du die Bons.

Übungen

  1. Wenn du im Versuchsaufbau das Ziel erreicht hast, reduzier auf 8 Bestellungen und finde heraus, ob du es auch mit 2 Herdplatten in unter 3 Sekunden schaffst, indem du Zeitlimit und Wiederholungen veränderst.
  2. Bau in den Prozessor aus 12-2 eine Pause zwischen den Wiederholungen ein (await asyncio.sleep(0.05 * 2 ** attempt)). Solltest du pausieren, bevor du die Herdplatte greifst, oder danach? Lass beides laufen und vergleich die Laufzeit.
  3. Such dir ein Programm aus, das du bauen möchtest, und plane es mit der Tabelle aus 12-1 und der Checkliste aus 12-4. Schreib für jedes Werkzeug eine Zeile, welches du nimmst und warum.

Rezeptkarte der Chefin

  • Zuerst fragen: Arbeit mit Warten (asyncio, Threads) oder Arbeit mit vollen Händen (Prozesse, 3.14t)?

  • Blockierendes in asyncio mit to_thread, schwere Berechnungen mit run_in_executor (Prozess-Pool).

  • Ressourcenlimit mit Semaphore, Zeitlimit mit asyncio.timeout, was zusammen enden muss, mit TaskGroup.

  • Wiederholen: Herdplatte abgeben und hinten anstellen. Zeitlimit: ‚länger als normal, kürzer als verpatzt‘.

  • Checkliste: gemeinsamer Zustand, Lock-Reihenfolge, blockierende Aufrufe, verschluckte Abbrüche, Tasks ohne Referenz, Queues, die nie fertig werden.

  • Eine Küche ist ein Programm. Zeichne die Balken, dann siehst du es.

← Zurück

Lektion 11 · Der Gastraum steht still: blockierende Aufrufe und Kombinieren

Über neue Lektionen informiert werden

Wir schicken dir eine E-Mail, wenn dieser Kurs neue Lektionen bekommt oder ein Folgekurs erscheint.

Erhoben: deine E-Mail-Adresse · Zweck: Hinweise auf neue Lektionen oder den Start dieses Kurses · Speicherdauer: bis du die Löschung verlangst. Keine andere Nutzung, keine Weitergabe. Antworte einfach auf eine Benachrichtigung, dann löschen wir sie sofort.

Fragen & Antworten

Frag alles zu dieser Lektion. Fragen aus allen Sprachversionen dieser Seite erscheinen hier gemeinsam.

Wird als reiner Text angezeigt, ohne Links oder Formatierung.

R

ReflexKR

Python und Reflex zum Anfassen. Alle Beispiele wurden mit Reflex 0.10.0 ausgeführt und geprüft.

ReflexKR ist kostenlos und ohne Werbebanner. Wenn es dir geholfen hat, kannst du es mit einem Kaffee unterstützen. ☕ Spendier uns einen Kaffee

Hinweis zu Affiliate-Links
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex