La principale distinzione, come fai notare nella tua domanda, è se lo scheduler prevarrà o meno un thread. Il modo in cui un programmatore pensa alla condivisione di strutture di dati o alla sincronizzazione tra "thread" è molto diverso nei sistemi preventivi e cooperativi.
In un sistema cooperativo (che va da molti nomi, cooperative multitasking , nonpreemptive multitasking , thread a livello utente , fili verdi , e le fibre sono cinque quelli comuni attualmente) il programmatore è garantito che il loro codice verrà eseguito atomicamente finché non effettuano chiamate di sistema o chiamate yield()
. Ciò rende particolarmente facile gestire le strutture dati condivise tra più fibre. A meno che non sia necessario effettuare una chiamata di sistema come parte di una sezione critica, non è necessario contrassegnare le sezioni critiche ( ad esempio con mutex lock
e unlock
chiamate). Quindi in codice come:
x = x + y
y = 2 * x
il programmatore non deve preoccuparsi che qualche altra fibra possa lavorare contemporaneamente con le variabili x
e y
. x
e y
verrà aggiornato atomicamente insieme dal punto di vista di tutte le altre fibre. Allo stesso modo, tutte le fibre potrebbero condividere una struttura più complicata, come un albero e una chiamata come tree.insert(key, value)
non dovrebbe essere protetta da alcun mutex o sezione critica.
Al contrario, in un sistema multithreading preventivo, come con thread realmente paralleli / multicore, è possibile ogni possibile interlacciamento di istruzioni tra thread a meno che non vi siano sezioni critiche esplicite. Un'interruzione e una prelazione potrebbero diventare tra due istruzioni qualsiasi. Nell'esempio sopra:
thread 0 thread 1
< thread 1 could read or modify x or y at this point
read x
< thread 1 could read or modify x or y at this point
read y
< thread 1 could read or modify x or y at this point
add x and y
< thread 1 could read or modify x or y at this point
write the result back into x
< thread 1 could read or modify x or y at this point
read x
< thread 1 could read or modify x or y at this point
multiply by 2
< thread 1 could read or modify x or y at this point
write the result back into y
< thread 1 could read or modify x or y at this point
Quindi, per essere corretti su un sistema preventivo, o su un sistema con thread veramente paralleli, è necessario circondare ogni sezione critica con una sorta di sincronizzazione, come un mutex lock
all'inizio e un mutex unlock
alla fine.
Le fibre sono quindi più simili alle librerie di I / O asincrone che ai thread preventivi o ai thread veramente paralleli. Lo scheduler delle fibre viene richiamato e può cambiare fibra durante le operazioni di I / O a lunga latenza. Ciò può offrire il vantaggio di più operazioni di I / O simultanee senza richiedere operazioni di sincronizzazione in sezioni critiche. Pertanto, l'uso delle fibre può forse avere una minore complessità di programmazione rispetto ai thread preventivi o veramente paralleli, ma la mancanza di sincronizzazione attorno alle sezioni critiche porterebbe a risultati disastrosi se si provasse a far funzionare le fibre in modo simultaneo o preventivo.