Esistono molte soluzioni per implementare i thread "user-space". Sia goroutines golang.org, thread verdi di Python, async di C#, processi di erlang, ecc. L'idea è di consentire la programmazione concorrente anche con un numero singolo o limitato di thread.
È un livello di astrazione. È più facile per molte persone cogliere questo concetto e usarlo più efficacemente in molti scenari. È anche più facile per molte macchine (presupponendo una buona astrazione), dal momento che il modello passa dalla larghezza alla trazione in molti casi. Con pthreads (ad esempio), hai tutto il controllo. Con altri modelli di threading, l'idea è di riutilizzare i thread, perché il processo di creazione di un'attività concorrente sia poco costoso e di utilizzare un modello di threading completamente diverso. È molto più facile digerire questo modello; c'è meno da imparare e misurare, ei risultati sono generalmente buoni.
Quello che non capisco è, perché i thread del sistema operativo sono così costosi? A mio avviso, in entrambi i casi è necessario salvare lo stack dell'attività (thread del sistema operativo o thread dell'utente), ovvero alcune decine di kilobyte, ed è necessario uno scheduler per spostarsi tra due attività.
Creare un thread è costoso e lo stack richiede memoria. Inoltre, se il tuo processo utilizza molti thread, il cambio di contesto può uccidere le prestazioni. I modelli di threading così leggeri sono diventati utili per una serie di motivi. La creazione di un thread del sistema operativo è diventata una buona soluzione per le attività di medie e grandi dimensioni, idealmente in numeri bassi. Questo è restrittivo e richiede molto tempo per essere mantenuto.
Un thread di task/thread pool/userland non deve preoccuparsi della commutazione del contesto o della creazione di thread. Spesso è "riutilizzare la risorsa quando diventa disponibile, se non è pronta ora - anche, determinare il numero di thread attivi per questa macchina".
Più comunemente (IMO), i thread di livello SO sono costosi perché non vengono utilizzati correttamente dagli ingegneri: ne esistono troppi e c'è un sacco di cambio di contesto, c'è competizione per lo stesso set di risorse, i compiti sono troppo piccoli Richiede molto più tempo per capire come utilizzare correttamente i thread del sistema operativo e come applicare al meglio il contesto dell'esecuzione di un programma.
Il sistema operativo fornisce entrambe queste funzioni gratuitamente.
Sono disponibili, ma non sono gratuiti. Sono complessi e molto importanti per una buona prestazione. Quando si crea un thread del SO, viene dato il tempo "presto" - tutto il tempo del processo viene diviso tra i thread. Questo non è il caso comune con i thread utente. L'attività viene spesso accodata quando la risorsa non è disponibile. Ciò riduce il cambio di contesto, la memoria e il numero totale di thread che devono essere creati. Quando l'attività termina, il thread ne riceve un altro.
Consideriamo questa analogia di distribuzione temporale:
- presuppongono che sei in un casinò. Ci sono un numero di persone che vogliono le carte.
- Hai un numero fisso di rivenditori. Ci sono meno rivenditori che persone che vogliono le carte.
- Non ci sono sempre abbastanza carte per ogni persona in un dato momento.
- Le persone hanno bisogno di tutte le carte per completare il proprio gioco/mano. Restituiscono le loro carte al mazziere quando il loro gioco/mano è completo.
Come chiederebbe ai rivenditori di distribuire le carte?
Sotto lo scheduler del sistema operativo, questo sarebbe basato sulla priorità (thread). A ogni persona verrà assegnata una carta alla volta (tempo della CPU) e la priorità verrà valutata continuamente.
Le persone rappresentano l'attività o il lavoro del thread. Le carte rappresentano il tempo e le risorse. I concessionari rappresentano discussioni e risorse.
Come gestiresti più velocemente se ci fossero 2 rivenditori e 3 persone? e se c'erano 5 concessionari e 500 persone? Come puoi minimizzare le carte esaurite da gestire? Con le discussioni, l'aggiunta di carte e l'aggiunta di rivenditori non è una soluzione che puoi fornire "su richiesta". L'aggiunta di CPU equivale all'aggiunta di rivenditori. L'aggiunta di thread è equivalente ai dealer che distribuiscono le carte a più persone alla volta (aumenta il cambio di contesto). Esistono diverse strategie per distribuire le carte più rapidamente, soprattutto dopo aver eliminato il bisogno di carte delle persone in un determinato periodo di tempo. Non sarebbe più veloce andare a un tavolo e occuparsi di una persona o di una persona fino a quando il gioco non sarà completo se il rapporto tra il dealer e le persone è di 1/50? Confrontalo con la visita di ogni tabella in base alla priorità e coordinando le visite tra tutti i rivenditori (l'approccio del sistema operativo). Ciò non vuol dire che il sistema operativo sia stupido - ciò implica che la creazione di un thread del sistema operativo è un ingegnere che aggiunge più persone e più tabelle, potenzialmente più di quanto i rivenditori possano ragionevolmente gestire. Fortunatamente, i vincoli possono essere risolti in molti casi utilizzando altri modelli di multithreading e astrazioni più elevate.
Perché i thread del sistema operativo dovrebbero essere più costosi dei fili "verdi"? Qual è la ragione del presunto peggioramento delle prestazioni causato dall'avere un thread OS dedicato per ogni "attività"?
Se avete sviluppato una libreria di threading prestazioni critiche di basso livello (ad esempio su di pthreads), si dovrebbe riconoscere l'importanza del riutilizzo (e implementarlo nella libreria come un modello disponibile per gli utenti). Da questo punto di vista, l'importanza dei modelli di multithreading di livello superiore è una soluzione/ottimizzazione semplice ed evidente basata sull'utilizzo del mondo reale e l'ideale che la barra di inserimento per l'adozione e l'utilizzo efficace del multithreading può essere ridotta.
Non è che siano costosi: il modello e il pool dei thread leggeri rappresentano la soluzione migliore per molti problemi e un'astrazione più appropriata per gli ingegneri che non capiscono bene i thread. La complessità del multithreading è notevolmente semplificata (e spesso più performante nell'uso del mondo reale) con questo modello. Con i thread del sistema operativo, si ha un maggiore controllo, ma occorre fare parecchie altre considerazioni per utilizzarli nel modo più efficace possibile - tenere conto di queste considerazioni può ripercuotere drammaticamente l'esecuzione/l'implementazione di un programma. Con astrazioni di livello più elevato, molte di queste complessità sono minimizzate modificando completamente il flusso dell'esecuzione dell'attività (larghezza contro pull).
Non sono solo considerati costosi, lo sono. Credo che alcuni fili verdi (di Haskell?) Pesino solo un paio di kilobyte ciascuno, cioè cento volte più piccoli. Un altro problema: i thread Python standard non sono verdi - hanno problemi con il multithreading a causa del GIL, ma sono comunque dei veri thread del sistema operativo (forse stai pensando a 'greenlets'?) Quella è una storia diversa, e in effetti simile al verde thread). – delnan
@delnan OK, l'ho sentito. Ma non sono ancora sicuro del perché dovrebbero essere più costosi. Entrambi devono salvare lo stack e fare il context switch (ignorare GIL, ci sono molti esempi non python). –