Per comprendere il threading in VB.NET, è utile comprendere alcuni dei concetti di base. Il primo è che il threading è qualcosa che accade perché il sistema operativo lo supporta. Microsoft Windows è un sistema operativo multitasking preventivo. Una parte di Windows denominata Utilità di pianificazione trasferisce il tempo del processore a tutti i programmi in esecuzione. Questi piccoli pezzi di tempo del processore sono chiamati fasce orarie. I programmi non sono responsabili di quanto tempo del processore ottengono, l'utilità di pianificazione è. Poiché questi intervalli di tempo sono così piccoli, si ha l'illusione che il computer stia facendo diverse cose contemporaneamente.
Definizione della discussione
Un thread è un singolo flusso sequenziale di controllo.
Alcuni qualificatori:
- Un thread è un "percorso di esecuzione" attraverso quel corpo di codice.
- I thread condividono la memoria, quindi devono cooperare per produrre il risultato corretto.
- Un thread ha dati specifici del thread come registri, un puntatore di stack e un contatore di programmi.
- Un processo è un singolo corpo di codice che può avere molti thread, ma ne ha almeno uno e ha un singolo contesto (spazio degli indirizzi).
Questa è roba a livello di assembly, ma è quello che ti viene in mente quando inizi a pensare ai thread.
Multithreading vs. multiprocessing
multithreading non è la stessa dell'elaborazione parallela multicore, ma il multithreading e il multiprocessing funzionano insieme. La maggior parte dei PC oggi ha processori che hanno almeno due core e le macchine domestiche ordinarie a volte hanno fino a otto core. Ogni core è un processore separato, in grado di eseguire programmi da solo. Si ottiene un aumento delle prestazioni quando il sistema operativo assegna un processo diverso a core diversi. L'uso di più thread e più processori per prestazioni ancora maggiori è chiamato parallelismo a livello di thread.
Molto di ciò che può essere fatto dipende da cosa possono fare il sistema operativo e l'hardware del processore sempre quello che puoi fare nel tuo programma e non dovresti aspettarti di poter usare più thread su qualunque cosa. In effetti, potresti non trovare molti problemi che beneficiano di più thread. Quindi, non implementare il multithreading solo perché è lì. Puoi facilmente ridurre le prestazioni del tuo programma se non è un buon candidato per il multithreading. Proprio come esempi, i codec video possono essere i programmi peggiori per il multithread perché i dati sono intrinsecamente seriale. I programmi server che gestiscono le pagine Web potrebbero essere tra i migliori perché i diversi client sono intrinsecamente indipendenti.
Praticare la sicurezza del filo
Il codice multithread richiede spesso una complessa coordinazione dei thread. I bug sottili e difficili da trovare sono comuni perché spesso thread diversi devono condividere gli stessi dati in modo che i dati possano essere modificati da un thread quando un altro non se lo aspetta. Il termine generale per questo problema è "condizioni di gara". In altre parole, i due thread possono entrare in una "gara" per aggiornare gli stessi dati e il risultato può essere diverso a seconda del thread "vince". A titolo di esempio banale, supponiamo che stai codificando un loop:
Se il contatore del loop "I" salta inaspettatamente il numero 7 e va da 6 a 8, ma solo qualche volta, avrebbe effetti disastrosi su qualunque cosa stia facendo il loop. Prevenire problemi come questo si chiama sicurezza dei thread. Se il programma necessita del risultato di un'operazione in un'operazione successiva, può essere impossibile codificare processi paralleli o thread per farlo.
Operazioni base di multithreading
È tempo di mettere in discussione questo discorso precauzionale e scrivere un codice multithreading. Questo articolo utilizza un'applicazione console per semplicità in questo momento. Se si desidera seguire, avviare Visual Studio con un nuovo progetto Applicazione console.
Lo spazio dei nomi principale utilizzato dal multithreading è il sistema. Lo spazio dei nomi di threading e la classe Thread creeranno, avvieranno e arresteranno nuovi thread. Nell'esempio seguente, notare che TestMultiThreading è un delegato. Cioè, devi usare il nome di un metodo che può chiamare il metodo Thread.
In questa app, avremmo potuto eseguire il secondo Sottotitolo semplicemente chiamandolo:
Ciò avrebbe eseguito l'intera applicazione in modo seriale. Il primo esempio di codice sopra, tuttavia, dà il via alla subroutine TestMultiThreading e quindi continua.
Un esempio di algoritmo ricorsivo
Ecco un'applicazione multithread che coinvolge il calcolo delle permutazioni di un array usando un algoritmo ricorsivo. Non tutto il codice è mostrato qui. La matrice di caratteri permutata è semplicemente "1", "2", "3", "4" e "5." Ecco la parte pertinente del codice.
Si noti che esistono due modi per chiamare il sub Permute (entrambi commentati nel codice sopra). Uno dà il via a un thread e l'altro lo chiama direttamente. Se lo chiami direttamente, otterrai:
Tuttavia, se si avvia un thread e si avvia invece il sottotitolo Permute, si ottiene:
Ciò mostra chiaramente che viene generata almeno una permutazione, quindi il sottotitolo principale si sposta in avanti e termina, visualizzando "Principale finito", mentre il resto delle permutazioni viene generato. Poiché il display proviene da un secondo sottotitolo chiamato dal sottotitolo Permute, sai che fa parte anche del nuovo thread. Questo illustra il concetto che un thread è "un percorso di esecuzione" come menzionato in precedenza.
Esempio di condizioni di gara
La prima parte di questo articolo menzionava una condizione di razza. Ecco un esempio che lo mostra direttamente:
La finestra immediata ha mostrato questo risultato in una prova. Altre prove erano diverse. Questa è l'essenza di una condizione di razza.