La maggior parte delle risposte sopra parla di prestazioni e operazioni simultanee. Ho intenzione di approcciarlo da una prospettiva diversa.
Prendiamo il caso, diciamo, di un semplicistico programma di emulazione terminale. Devi fare le seguenti cose:
- cercare i caratteri in arrivo dal sistema remoto e visualizzarli
- guarda le cose che arrivano dalla tastiera e inviale al sistema remoto
(Gli emulatori di terminali reali fanno di più, incluso potenzialmente l'eco delle cose digitate anche sul display, ma per ora lo passeremo sopra.)
Ora il ciclo per la lettura dal telecomando è semplice, secondo il seguente pseudocodice:
while get-character-from-remote:
print-to-screen character
Anche il loop per il monitoraggio della tastiera e l'invio è semplice:
while get-character-from-keyboard:
send-to-remote character
Il problema, tuttavia, è che devi farlo simultaneamente. Il codice ora deve apparire più simile a questo se non hai thread:
loop:
check-for-remote-character
if remote-character-is-ready:
print-to-screen character
check-for-keyboard-entry
if keyboard-is-ready:
send-to-remote character
La logica, anche in questo esempio deliberatamente semplificato che non tiene conto della complessità delle comunicazioni nel mondo reale, è piuttosto offuscata. Con il threading, tuttavia, anche su un singolo core, i due loop pseudocodici possono esistere indipendentemente senza intrecciare la loro logica. Poiché entrambi i thread saranno per lo più associati a I / O, non caricano molto la CPU, anche se, a rigore, sono più dispendiosi di risorse della CPU rispetto al loop integrato.
Ora, ovviamente, l'uso nel mondo reale è più complicato di quanto sopra. Ma la complessità del ciclo integrato aumenta esponenzialmente man mano che si aggiungono ulteriori preoccupazioni all'applicazione. La logica diventa sempre più frammentata e devi iniziare a usare tecniche come macchine a stati, coroutine, ecc. Per rendere le cose gestibili. Gestibile, ma non leggibile. Il threading rende il codice più leggibile.
Quindi perché non dovresti usare il threading?
Bene, se le tue attività sono associate alla CPU anziché agli I / O, il threading rallenta effettivamente il tuo sistema. Le prestazioni ne risentiranno. Molto, in molti casi. ("Thrashing" è un problema comune se si rilasciano troppi thread associati alla CPU. Si finisce per passare più tempo a modificare i thread attivi rispetto a quando si esegue il contenuto dei thread stessi.) Inoltre, uno dei motivi per cui la logica sopra è così semplice è che ho scelto molto deliberatamente un esempio semplicistico (e non realistico). Se volevi fare eco a ciò che è stato digitato sullo schermo, hai un nuovo mondo di sofferenza mentre introduci il blocco delle risorse condivise. Con una sola risorsa condivisa questo non è tanto un problema, ma inizia a diventare un problema sempre più grande poiché hai più risorse da condividere.
Quindi, alla fine, il threading riguarda molte cose. Ad esempio, si tratta di rendere i processi associati agli I / O più reattivi (anche se complessivamente meno efficienti) come alcuni hanno già detto. Si tratta anche di rendere la logica più facile da seguire (ma solo se si minimizza lo stato condiviso). Si tratta di molte cose e devi decidere se i suoi vantaggi superano i suoi svantaggi caso per caso.