Gebruikershulpmiddelen

Site-hulpmiddelen


asyncio

Python Asyncio/Threading

πŸ—‚οΈ Terug naar start
⭐ πŸ—‚οΈ Python Start

πŸš€ πŸ•› Nest Asyncioin streamlit

Threading en asyncio (soms met nest_asyncio) worden allebei gebruikt om meerdere dingen tegelijk te laten gebeuren. Maar ze werken op totaal verschillende manieren.

⭐ Op deze pagina een voorbeeld //(in een Class)// van een werkende versie die werkt met Python en Streamlit als GUI

Threading

De module threading in Python gebruik je om meerdere dingen tegelijk te laten gebeuren binnen één programma. Dat heet multithreading. Dit is vooral handig wanneer je programma veel wacht op iets, zoals op:

  • een bestand dat wordt ingelezen of geschreven;
  • een netwerkverzoek (zoals een API-aanroep);
  • een gebruiker die iets moet invoeren;
  • een time.sleep() of wachttijd;
  • langzame hardware zoals seriΓ«le communicatie (bijv. met een sensor of Arduino).

πŸ”§ Wanneer gebruik je threading?

Gebruik threading:

  • als je niet intensief rekent, maar juist wacht op dingen;
  • als je wil dat je programma niet vastloopt tijdens een wachttijd;
  • als je een achtergrondtaak wil laten lopen terwijl je hoofdprogramma doorgaat.

🧠 Simpele uitleg met analogie

Stel: je bent een kok in een keuken. Je zet water op voor pasta (dat duurt 10 minuten). In plaats van 10 minuten te wachten, begin je alvast groenten te snijden. Je doet dus meerdere taken tegelijk.

Zonder threading wacht je tot het water kookt, en doe je pas daarna het snijwerk.

βœ… Voorbeeld 1: taak op de achtergrond

import threading
import time

def achtergrondtaak():
    for i in range(5):
        print("Achtergrondtaak draait...", i)
        time.sleep(1)

# Start een thread
thread = threading.Thread(target=achtergrondtaak)
thread.start()

# Hoofdprogramma gaat door
for i in range(3):
    print("Hoofdprogramma doet iets anders...", i)
    time.sleep(1)

# Wacht tot de thread klaar is
thread.join()
print("Alles klaar!")

Wat gebeurt hier?

  • De achtergrondtaak() draait in een aparte thread;
  • Het hoofdprogramma doet ondertussen iets anders;
  • thread.join() zorgt dat we wachten tot de thread klaar is.

βœ… Voorbeeld 2: twee dingen tegelijk

def print_hello():
    for _ in range(3):
        print("Hallo!")
        time.sleep(1)

def print_world():
    for _ in range(3):
        print("Wereld!")
        time.sleep(1)

t1 = threading.Thread(target=print_hello)
t2 = threading.Thread(target=print_world)

t1.start()
t2.start()

t1.join()
t2.join()

Je krijgt dan een mix van β€œHallo!” en β€œWereld!”, afhankelijk van de timing.

⚠️ Belangrijk om te weten

  1. Let op gedeelde variabelen
    • Threads kunnen tegelijk aan dezelfde data zitten. Gebruik dan threading.Lock() om problemen (race conditions) te voorkomen.
  2. Niet geschikt voor zware berekeningen
    • Door de Global Interpreter Lock (GIL) in CPython is threading niet geschikt voor zware rekenklussen. Gebruik dan liever multiprocessing.

🧠 Waar kun je threading voor inzetten?

Hier zijn wat praktische toepassingen:

  • Een GUI app waarbij je netwerkverkeer doet zonder dat het scherm β€œbevriest”;
  • Een webscraper die meerdere pagina's tegelijk ophaalt;
  • Een game waarin vijanden op de achtergrond bewegen;
  • Een server die meerdere clients tegelijk moet afhandelen;
  • Een sensorapp (bijv. via seriΓ«le poort) die data blijft uitlezen terwijl het hoofdprogramma doorgaat.

Loader

Je kunt threading prima gebruiken om een loader of wachtschermpje te laten draaien terwijl je AI-chatbot bezig is met denken of antwoorden ophalen. Zo lijkt je app sneller en reageert deze beter op de gebruiker.

βœ… Voorbeeld: loader tijdens AI-berekening

Stel: je AI-bot doet iets tijdrovends, zoals een OpenAI-aanroep. Je wilt intussen een draaiende β€œloader” weergeven.

import threading
import time
import sys

def loader(stop_event):
    chars = "|/-\\"
    i = 0
    while not stop_event.is_set():
        print(f"\rWachten op antwoord... {chars[i % len(chars)]}", end="", flush=True)
        i += 1
        time.sleep(0.1)
    print("\rAntwoord ontvangen!           ")

def ai_antwoord():
    # Simuleert een trage AI-actie, bv. OpenAI API-call
    time.sleep(5)
    return "Dit is het AI-antwoord."

# Event om de loader te stoppen
stop_event = threading.Event()
loader_thread = threading.Thread(target=loader, args=(stop_event,))
loader_thread.start()

# Voer AI-actie uit
antwoord = ai_antwoord()

# Stop de loader
stop_event.set()
loader_thread.join()

# Toon antwoord
print(antwoord)

πŸ’‘ Wat gebeurt hier?

  • De loader() draait in een aparte thread en laat een draaiende animatie zien.
  • Zodra ai_antwoord() klaar is, wordt het stop_event geactiveerd.
    • Dan stopt de loader en laat je het echte antwoord zien.

Asyncio

asyncio is een ingebouwde module in Python waarmee je asynchrone (concurrente) code kunt schrijven. Het maakt gebruik van coroutines, die met het sleutelwoord async worden gedefinieerd, en van het await-statement om te wachten op asynchrone operaties (zoals I/O-bound taken) zonder de hele applicatie te blokkeren. Met een event loop worden deze coroutines efficiΓ«nt gepland en uitgevoerd. Dit is bijzonder handig voor toepassingen waar je veel wachttijd hebt, zoals bij netwerkcommunicatie of bestand I/O, omdat je zo meerdere taken tegelijk kunt laten draaien.

import asyncio

class AsyncWorker:
    def __init__(self, name):
        self.name = name

    async def async_task(self, seconds):
        print(f"{self.name}: Taak gestart, wacht {seconds} seconden.")
        # Simuleer een I/O-bound operatie met asyncio.sleep
        await asyncio.sleep(seconds)
        print(f"{self.name}: Taak voltooid na {seconds} seconden.")
        return f"{self.name} klaar na {seconds} seconden"

    async def run_tasks(self):
        print(f"{self.name}: Start meerdere taken...")
        # Start twee asynchrone taken en wacht tot beide klaar zijn
        results = await asyncio.gather(
            self.async_task(2),
            self.async_task(3)
        )
        print(f"{self.name}: Alle taken voltooid. Resultaten: {results}")

# Het uitvoeren van de asynchrone taken via de event loop
if __name__ == "__main__":
    worker = AsyncWorker("Worker1")
    asyncio.run(worker.run_tasks())

Uitleg van de code:

  1. De class AsyncWorker:
    • De
      __init__

      -methode initialiseert een instantie met een naam.

    • async_task: Dit is een coroutine die een taak simuleert door een aantal seconden te wachten met await asyncio.sleep(seconds).
    • run_tasks: Deze coroutine start meerdere taken tegelijk met asyncio.gather en wacht totdat ze allebei voltooid zijn.
  2. Event loop starten:
    • Met asyncio.run(worker.run_tasks()) wordt de event loop gestart en de coroutine run_tasks uitgevoerd.

Dit voorbeeld laat zien hoe je asyncio kunt gebruiken binnen een class om meerdere asynchrone taken tegelijk te laten draaien. Hierdoor kun je efficiΓ«nter omgaan met taken die wachten op externe bronnen zonder dat je applicatie blokkeert.

import asyncio

class AsyncApp:
    def __init__(self, name):
        self.name = name
        # Start de asynchrone taken direct bij initialisatie
        self.start()

    async def async_task(self, seconds):
        print(f"{self.name}: Taak gestart, wacht {seconds} seconden.")
        await asyncio.sleep(seconds)
        print(f"{self.name}: Taak voltooid na {seconds} seconden.")
        return f"Resultaat van {seconds} seconden"

    async def main(self):
        print(f"{self.name}: Start meerdere taken...")
        # Voer meerdere taken gelijktijdig uit
        results = await asyncio.gather(
            self.async_task(1),
            self.async_task(2),
            self.async_task(3)
        )
        print(f"{self.name}: Alle taken voltooid. Resultaten: {results}")

    def start(self):
        # Start de event loop en voer de main coroutine uit
        asyncio.run(self.main())

# Wanneer dit script direct wordt uitgevoerd, maakt het een instance van AsyncApp aan.
if __name__ == "__main__":
    app = AsyncApp("AsyncAppInstance")

Asyncio gebruik in Streamlit app

threading en asyncio

Threading en asyncio (soms met nest_asyncio) worden allebei gebruikt om meerdere dingen tegelijk te laten gebeuren. Maar ze werken op totaal verschillende manieren. Hieronder leg ik het verschil duidelijk uit, met voorbeelden en wanneer je wat moet gebruiken.


🧡 threading: echte parallelliteit met meerdere OS-threads

Elke thread draait letterlijk tegelijk (losse werkeenheden).

  • Geschikt voor wachten op externe dingen (I/O: netwerk, files), en voor eenvoudige parallelliteit zonder veel regels.
  • Elke thread kan geblokkeerd worden zonder dat andere threads stoppen.
  • Werkt goed met bestaande (blokkerende) bibliotheken zoals requests, input() enz.

βœ… Gebruik threading als:

  • Je niet met async def of await werkt;
  • Je code blokkerende functies gebruikt (zoals time.sleep(), requests.get(), enz.);
  • Je iets simpels wil doen zonder veel structuur of controlestromen.

βš™οΈ asyncio: cooperatieve, niet-blokkerende multitasking binnen één thread

  • Werkt met een event loop waarin taken vrijwillig pauzeren met await.
  • Niet β€œecht” parallel – maar super efficiΓ«nt bij veel kleine I/O-wachttijden.
  • Je gebruikt async def, await, en asyncio.create_task() om meerdere dingen tegelijk te laten lopen.

βœ… Gebruik asyncio als:

  • Je bibliotheken gebruikt die asynchroon zijn (zoals aiohttp, openai.AsyncOpenAI);
  • Je applicatie veel tegelijk moet doen zonder blokkerende code;
  • Je volledige controle wil over de volgorde van taken en pauzemomenten;
  • Je een modernere (snellere) aanpak wil binnen één thread.

πŸ“¦ nest_asyncio: maakt asyncio herbruikbaar in een bestaande sync omgeving

  • Normaal mag je maar één keer een event loop starten.
    • In bijvoorbeeld Jupyter of streamlit is er al een loop actief.
  • nest_asyncio laat je toch await gebruiken binnen die bestaande loop (meestal een hack).

βœ… Gebruik nest_asyncio alleen als:

  • Je in een omgeving zit waar al een event loop draait (zoals Jupyter, notebooks, Streamlit);
  • Je asynchrone functies toch wilt kunnen gebruiken in een β€œgewone” omgeving.

πŸ” Praktisch verschil threading/asyncio

Stel je maakt een chatbot die een OpenAI-aanroep doet en tegelijk een loader laat zien:

Met threading

# gewone functie
def openai_call(): 
    return requests.post("...")  # blokkeert de thread

# loader in andere thread: threading.Thread

Met asyncio

# async functie
async def openai_call(): 
    return await aiohttp.post("...")  # niet-blokkerend

# loader tegelijk met asyncio.create_task(loader())

Bij asyncio moet alles β€” van de HTTP-client tot je eigen code β€” non-blocking zijn.

πŸ€” Dus... wanneer gebruik je wat?

Situatie Gebruik
———————————————– β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”-
Werkt met `requests`, `input()`, `time.sleep()` `threading`
Werkt met `aiohttp`, `async def`, `await` `asyncio`
Je wil eenvoudig een loader starten `threading` is vaak simpeler
Je bouwt een schaalbare server/app `asyncio` (sneller en moderner)
Jupyter Notebook + asyncio `asyncio` + `nest_asyncio`

πŸ”„ Mixen?

Je kunt ze combineren, maar wees voorzichtig:

  • Gebruik threading voor blokkerende functies in een asyncio-omgeving (via run_in_executor);
  • Gebruik asyncio voor de rest, als je bibliotheken async ondersteunen.

asyncio.txt Β· Laatst gewijzigd: door a3dijke