SlideShare a Scribd company logo
1 of 28
Download to read offline
Scrivere Applicazioni Multi Threading in .NET
Di Massimiliano Brolli
Premessa...................................................................................................3
Prerequisiti ................................................................................................3
Il Multi Threading .......................................................................................4
Le basi ......................................................................................................5
Il Namespace ..........................................................................................5
Le Dichiarazioni .......................................................................................5
Lanciare un thread ...................................................................................5
Meccanismi di Sincronizzazione ....................................................................6
Sleep......................................................................................................6
Start ......................................................................................................7
ResetAbort..............................................................................................7
Suspend .................................................................................................7
Resume ..................................................................................................7
Join........................................................................................................8
Interrupt.................................................................................................8
Abort......................................................................................................9
SpinWait............................................................................................... 10
Stato dei thread ....................................................................................... 11
La priorità dei thread..............................................................................13
Scopo delle variabili ...............................................................................15
Pool di Thread .......................................................................................... 18
Gli eventi................................................................................................. 22
I Timer.................................................................................................... 24
Apartment Thread e interoperabilità COM .................................................... 25
Conclusioni .............................................................................................. 27
Bibliografia .............................................................................................. 28
Premessa
Molto spesso ci siamo imbattuti nel termine AJAX, acronimo di Asynchronous
JavaScript and XML ovvero un sistema che permette alle pagine web di agire in
maniera asincrona.
Lo scopo di questo documento e far capire tramite semplici esempi le
componenti/funzionalità offerte da questo straordinario Framework messo a
disposizione anche in ambiente .NET.
Prerequisiti
Per procedere alla lettura di questo documento occorre aver letto le seguenti
dispense :
• Introduzione agli algoritmi.
• La scelta tra Microsoft.NET & Java
• Predisposizione Virtual Machine di base
• Predisposizione Virtual Machine per l'ambiente di sviluppo Microsoft .NET
• Introduzione alla scrittura del codice .NET
• Scrivere applicazioni .NET con Visual Studio
• Panoramica sulla programmazione ad oggetti
• La Storia
• Introduzione al Framework .NET
• Gli oggetti ADO.NET
• Predisposizione Ambiente SQL Server
• Applicazioni Windows Forms
• ADO.NET Entity Framework
• Scrivere ASP.NET Web Application, premessa e predisposizione
• Scrivere ASP.NET Web Application, La prima applicazione Web Forms
• Scrivere ASP.NET Web Application, I Web Services
• Scrivere ASP.NET Web Application, Applicazioni MVC
• Scrivere ASP.NET Web Application, AJAX
Il Multi Threading
Il MultiThreading comparso con Windows NT 3.1 e successivamente con Windows
95 rappresenta una rivoluzione nel campo informatico di largo consumo.
Solamente i linguaggi di basso livello prima dell’evento del Framework .NET
potevano realizzare applicazioni MultiThreading come ad esempio MSVC++ con
l’ausilio di particolari librerie.
In questo documento andremo ad analizzare le istruzioni che occorre imparare
per lavorare con i Thread utilizzando Microsoft.NET Framework 2.0 ed il
linguaggio VB.NET.
Un Thread o filo in inglese, rappresenta un processo che può lavorare
parallelamente al padre chiamante.
Nel framework.NET si può far lavorare in un Thread separato porzioni di codice
quali Sub Routine o intere classi per realizzare applicazioni performanti che
hanno come limite solamente la fantasia.
Addentrandoci nel documento cominceremo a familiarizzare con i meccanismi di
programmazione Asincrona che introdurranno problematiche nuove quali la
concorrenza applicativa e i meccanismi di sincronizzazione.
Le basi
Il Namespace
Per realizzare applicazioni MultiThreading occorre utilizzare il nameSpace
System.Threading come viene mostrato nell’esempio successivo.
Imports System.Threading
Le Dichiarazioni
Per lavorare con i Thread occorre dichiarare un oggetto di tipo ThreadStart che
permette di eseguire lo Start di un Thread e un oggetto Thread che ci
permetterà di crearlo.
Private _ThreadStart As ThreadStart
Private _Thread As Thread
Lanciare un thread
Come detto in precedenza è possibile far lavorare su un thread separato porzioni
di codice appartenenti ad una Sub Routine oppure una intera classe. Nell’esempio
successivo viene lanciato in un Thread separato una Sub Routine ClientGoSend.
L’istruzione _ClientThread.Start permetterà di lanciare il Thread come viene
mostrato nell’esempio successivo.
_ThreadStart = New ThreadStart(AddressOf ClientGoSend)
_Thread = New Thread(_ThreadStart)
_Thread.Start
Public sub ClientGoSend
End sub
Meccanismi di Sincronizzazione
I Thread essendo delle entità separate rispetto al processo chiamante che li ha
creati hanno una loro vita, questo vuol dire che mentre il padre effettuerà ad
esempio un loop infinito il Thread creato in precedenza prosegue in parallelo la
propria attività.
Questo meccanismo introduce alla programmazione Asincrona che seppur utile
aggiunge delle problematiche di sincronismo alle nostre applicazioni.
Il .NET Framework ci mette a disposizione dei metodi nella classe thread che
permettono di attendere per un determinato tempo un qualche evento oppure
ripartire nell’elaborazione allo scatenare di un altro.
Sleep
L’istruzione Thread.Sleep(100) mette in attesa il thread per un valore in
Millisecondi.
_ThreadStart = New ThreadStart(AddressOf Send)
_Thread = New Thread(_ThreadStart)
_Thread.Start
Console.WriteLine(“Main” & AppDomain.GetCurrentThreadId())‘del thread Main
Public sub Send
Dim objThread As Thread = Thread.CurrentThread()
objThread.Sleep(100) ‘Mette in attesa il thread per 100 Millisecondi
Console.WriteLine(“Thread” & AppDomain.GetCurrentThreadId()) ‘ID del thread
objThread.Abort ‘Distrugge il Thread
End sub
L’istruzione Console.WriteLine(AppDomain.GetCurrentThreadId()) viene
ripetuta per due volte all’interno dell’applicazione perché stamperà a console
prima l’ID del processo chiamante e poi l’ID del processo chiamato.
Il risultato sarà
Main 2574
Thread 2575
Oppure
Thread 2575
Main 2574
Questo perchè potrà eseguire l’istruzione Console prima il Main oppure prima il
Thread creato.
Di seguito vengono riportati dei metodi della classe thread che servono per la
sincronizzazione dei Thread.
Start
L’istruzione Start lancia un thread.
ResetAbort
L’istruzione ResetAbort interrompe la distruzione di un thread.
Suspend
L’istruzione Suspend mette in attesa il thread.
Resume
L’istruzione Resume riattiva il thread.
Join
L’istruzione Join mette in attesa il thread.
Nell’esempio sotto riportato, il codice Main chiama un thread che terminirà allo
scadere di 4 secondi e scriverà “Worker End”.
Imports System.Threading
Module Esempio01
Private _ThreadStart As ThreadStart
Private _Thread As Thread
Public Sub Worker()
Thread.Sleep(4000)
Console.WriteLine("Worker End")
End Sub
Sub Main()
_ThreadStart = New ThreadStart(AddressOf Worker)
_Thread = New Thread(_ThreadStart)
_Thread.Start()
_Thread.Join() 'Attende che il thread sia completato
Console.WriteLine("Main End")
End Sub
End Module
Eseguendo il codice vedremo che il risultato sarà il seguente
Worker End
Main End
Questo perché il main attenderà il completamento del thread _Thread prima di
effettuare la scrittura di “Main End” grazie al metodo Join.
Se asterischiamo l’istruzione _Thread.Join() e rieseguiamo l’esempio noteremo che il
risultato sarà il seguente.
Main End
Worker End
Questo perché il Main non attenderà la conclusione del Thread _Thread che scriverà
dopo 4 secondo il messaggio “Worker End”
Il metodo Join può prendere in input un parametro espresso in millisecondi/Timestamp.
Tali millisecondi/Timestamp stanno ad indicare il tempo massimo che verrà atteso per la
terminazione del Thread.
Interrupt
L’istruzione Interrupt interrompe lo stato di attesa di un thread.
Abort
L’istruzione Abort distrugge il thread.
E’ da notare che tale istruzione non è una garanzia di distruzione in quanto genera al
suo interno un’eccezione di tipo ThreadAbortException e imposta lo stato su
AbortRequested.
Se all’interno di questa eccezione venisse invocato il metodo ResetAbort verrebbe
vanificata la chiamata di Abort precedentemente invocata e quindi è importante fare
attenzione al codice che stiamo implementando tenendo conto di questi meccanismi di
concorrenza.
La stessa cosa vale per il metodo Interrupt che a sua volta genera un’eccezione di tipo
ThreadInterruptedException e imposta lo stato su Running anche se interromperà
l’esecuzione.
Il codice seguente mostra il comportamento di quanto descritto sopra.
Imports System.Threading
Module Esempio02
Private _ThreadStart As ThreadStart
Private _Thread As Thread
Sub Main()
_ThreadStart = New ThreadStart(AddressOf Worker)
_Thread = New Thread(_ThreadStart)
_Thread.Start()
_Thread.Abort() 'Da provare cambiandolo in Interrupt
Console.WriteLine("Main End")
End Sub
Public Sub Worker()
Dim objThread As Thread = Thread.CurrentThread()
Try
Console.WriteLine("Worker Sleeping")
Thread.Sleep(4000)
'generata se invocato il metodo interrupt
Catch e As ThreadInterruptedException
Console.WriteLine("Worker interrupt : ThreadState " & _
Thread.CurrentThread.ThreadState.ToString)
'generata se invocato il metodo Abort
Catch e As ThreadAbortException
Console.WriteLine("Worker Abort : ThreadState " & _
Thread.CurrentThread.ThreadState.ToString)
'operazioni conclusive del costrutto Try/Catch
Finally
Console.WriteLine("Worker End : ThreadState " & _
Thread.CurrentThread.ThreadState.ToString)
End Try
Console.WriteLine("Worker Completed")
End Sub
End Module
Eseguendolo il risultato sarà il seguente
Worker Sleeping
Worker Abort : ThreadState AbortRequested
Worker End : ThreadState AbortRequested
Main End
Da notare che il thread non è stato completato ( non viene notificato “Worker
Completed”) in quanto è stata richiesta una operazione di Abort che viene anche
notificata dall’istruzione Thread.CurrentThread.ThreadState.ToString che ci
mostra lo stato del thread impostato su AbortRequested.
Se al posto di _ClientThread.Abort() inserissimo _ClientThread.Interrupt()
nel main del modulo il risultato cambierà in questo modo.
Worker Sleeping
Main End
Worker interrupt : ThreadState Running
Worker End : ThreadState Running
Worker Completed
Da notare che il motodo Interrupt ha interrotto l’esecuzione del thread senza
distruggerlo, infatti in questo esempio viene notificata la terminazione del thread
dal messaggio “Worker Completed” che ci avverte che l’invocazione di interrupt
ha interrotto lo stato di Sleep del thread.
SpinWait
L’istruzione SpinWait viene utilizzata su applicazione che girano su macchine
multiprocessore e costituisce un’alternativa al comando Sleep che restituisce cicli
di CPU rimanendo in attesa.
Stato dei thread
I Thread possono assumere diversi stati in funzione della loro sincronizzazione
con i processi chiamanti. In questa sezione andremo ad analizzare i diversi stati
per capirne meglio il loro utilizzo.
Come visto in precedenza, per acquisire lo stato di un thread occorre accedere al
metodo Shared Thread.CurrentThread.ThreadState.ToString che ci ritorna
un valore in formato Stringa dello stato corrente del thread.
Running
La parola stessa ci dice che il thread sta in esecuzione
Imports System.Threading
Module Esempio03
Sub Main()
Dim _ThreadStart As New ThreadStart(AddressOf Worker)
Dim _Thread As New Thread(_ThreadStart)
_Thread.Start()
While True
Thread.Sleep(1000)
Console.WriteLine("Worker End : ThreadState " & _
_Thread.ThreadState.ToString)
End While
Console.WriteLine("Main End")
End Sub
Public Sub Worker()
While True
End While
Console.WriteLine("Worker Completed")
End Sub
End Module
WaitSleepJoin
Questo stato ci comunica che il thread si trova in uno stato di
Wait, Sleep o Join.
Imports System.Threading
Module Esempio04
Sub Main()
Dim _ThreadStart As New ThreadStart(AddressOf Worker)
Dim _Thread As New Thread(_ThreadStart)
_Thread.Start()
While True
Thread.Sleep(1000)
Console.WriteLine("Worker End : ThreadState " & _
_Thread.ThreadState.ToString)
End While
Console.WriteLine("Main End")
End Sub
Public Sub Worker()
While True
Thread.Sleep(1000)
End While
Console.WriteLine("Worker Completed")
End Sub
End Module
In questo caso è lo stesso Thread che sta effettuando un ciclo While
true ogni un secondo.
AbortRequested
La parola stessa ci dice che per il thread è stata invocata una
Abort.
Per il metodo Abort vi rimando all’esempio del metodo Abort nella
sezione Meccanismi di Sincronizzazione
La priorità dei thread
In un ambiente Multi Threading è possibile impostare una priorità di esecuzione
per ogni singolo thread. Tale meccanismo permette di condividere nel migliore
dei modi i cicli di CPU della macchina permettendo ai processi critici di avere una
maggiore priorità in termine di esecuzione sui processi secondari o di minor
importanza applicativa.
I processi Microsoft .NET sono di due tipi, processi di Foreground e processi di
Background.
Ai processi di Foreground possiamo attribuire una priorità di esecuzione in base
ad una Enumerazione contenuta nella classe System.threading.ThreadPriority
qua sotto ricreata per maggiore chiarezza.
‘Enumeration che riporta le priorità per i thread di Foreground
Public Enum Priority
Lowest = 0
BelowNormal = 1
Normal = 2
AboveNormal = 3
Highest = 4
End Enum
Nell’esempio successivo abbiamo utilizzato il metodo Priority per assegnare una
priorità di esecuzione. E’ importante dire che un Thread creato dal Runtime senza
specifica di priorità viene creato come Foreground di tipo Normal.
Imports System.Threading
Module Esempio05
Sub Main()
Dim _ThreadStart As New ThreadStart(AddressOf Worker)
Dim _Thread As New Thread(_ThreadStart)
_Thread.Start()
_Thread.Priority = ThreadPriority.Lowest
Console.WriteLine("Main End")
End Sub
Public Sub Worker()
Thread.Sleep(4000)
Console.WriteLine("Worker Completed, Priority is : " & _
Thread.CurrentThread.Priority.ToString)
End Sub
End Module
Il risultato di questo esempio ritornerà il seguente Output.
Worker Completed, Priority is : Lowest
Main End
Cambiando il tipo di Priorità da Lowest a Highest noteremo che l’applicazione
attenderà sempre la conclusione del thread prima di essere terminata
(Ovviamente omettendo il metodo Join).
Questo perché si tratta di un thread di tipo Foreground e quindi deve essere per
forza terminato prima di terminare l’intero processo.
E’ possibile definire un Thread come processo di Background utilizzando la
proprietà Thread.IsBackground di tipo Boleano.
Impostando a True questo valore il processo potrà essere terminato in qualsiasi
momento, talei processi di background saranno processi non di vitale importanza
per le nostre applicazioni.
Imports System.Threading
Module Esempio06
Sub Main()
Dim _ThreadStart As New ThreadStart(AddressOf Worker)
Dim _Thread As New Thread(_ThreadStart)
_Thread.Start()
_Thread.IsBackground = True
Console.WriteLine("Main End")
End Sub
Public Sub Worker()
Thread.Sleep(4000)
Console.WriteLine("Worker Completed, Priority is : " & _
Thread.CurrentThread.Priority.ToString)
End Sub
End Module
Il risultato di questo esempio ritornerà il seguente Output.
Main End
Questo perchè il processo figlio è stato definito di Background e quindi è stato
terminato senza preavviso dal processo chiamante.
Scopo delle variabili
E’ possibile creare delle variabili che avranno uno scopo limitato al processo che
le sta eseguendo. Nell’esempio che viene riportato in seguito, la variabile di
classe Value viene dichiarata inserendo il suffisso <ThreadStatic()>. Questa
variabile non sarà più una variabile condivisa sia dal processo chiamante che dal
processo figlio, ma ogni processo avrà una copia di tale variabile e ne potrà fare
un uso esclusivo.
Imports System.Threading
Module Esempio07
<ThreadStatic()> Public Value As String = "Main"
Sub Main()
Console.WriteLine("Main Value = " & Value)
Dim _ThreadStart As New ThreadStart(AddressOf Worker)
Dim _Thread As New Thread(_ThreadStart)
_Thread.Start()
_Thread.Join()
Console.WriteLine("Main Value = " & Value)
End Sub
Public Sub Worker()
Console.WriteLine("Worker Value = " & Value)
Value = "Worker"
Console.WriteLine("Worker Value = " & Value)
End Sub
End Module
Il risultato di questo esempio ritornerà il seguente Output.
Main Value = Main
Worker Value =
Worker Value = Worker
Main Value = Main
Main End
Vediamo come la variabile Value impostata a “Main” sarà visibile solamente nel
Main del programma, mentre il Thread Worker ne potrà fare un uso esclusivo
impostando un valore che non verrà condiviso con il programma Main chiamante.
E’ possibile utilizzando I Thread definire dei punti di codice nel quale non possono
accedervi più Thread contemporaneamente.
Questo è possibile grazie all’istruzione SyncLock.
Nell’esempio successivo verrà definita una variabile di classe di nome Lock e nel
metodo Worker verrà utilizzata l’istruzione SyncLock Lock che tramite l’oggetto
globale Lock determinerà chi far accedere alla porzione di codice specifica.
L’oggetto Lock deve essere definito di tipo Object.
Imports System.Threading
Module Esempio15
Public Counter As Integer
Public Lock As New Object
Sub Main()
Dim i As Integer
For i = 0 To 10
Dim _ThreadStart As New ThreadStart(AddressOf Worker)
Dim _Thread As New Thread(_ThreadStart)
_Thread.Start()
Next
End Sub
Public Sub Worker()
‘SyncLock Lock
Counter = Counter + 1
Thread.Sleep(New Random().Next(500, 2000))
Console.WriteLine("Worker Counter : " & Counter)
‘End SyncLock
End Sub
End Module
Il risultato di questo esempio ritornerà il seguente Output.
Questo perché la display del contatore avverrà dopo che tutti i thread avranno
effettuato la somma.
Worker Counter : 11
Worker Counter : 11
Worker Counter : 11
Worker Counter : 11
Worker Counter : 11
Worker Counter : 11
Worker Counter : 11
Worker Counter : 11
Worker Counter : 11
Worker Counter : 11
Worker Counter : 11
Se disasterischiamo le istruzioni SyncLock e rieseguiamo l’esempio vedremo che
il risultato cambierà in questo modo.
Worker Counter : 1
Worker Counter : 2
Worker Counter : 3
Worker Counter : 4
Worker Counter : 5
Worker Counter : 6
Worker Counter : 7
Worker Counter : 8
Worker Counter : 9
Worker Counter : 10
Worker Counter : 11
Questo perché il blocco di codice ha un lock determinato dall’oggetto Lock
definito come variabile di classe che ne restituisce il sincronismo.
Da notare che il primo thread accederà al blocco e gli altri rimarranno in attesa
fino a quando il primo thread non accederà all’istruzione End SyncLock, a quel
punto il secondo entrerà in azione e così via.
Pool di Thread
I Pool di Thread vengono in nostro aiuto quando occorre creare dei processi di
lunga durata che lavorano in parallelo. Un classico esempio sono quelle
applicazioni Server (Web Server, SMTP Server ecc..) che non possono smaltire
una richiesta alla volta ma debbono prendere in carico le richieste e processarle
in parallelo.
Quindi l’utilizzo di Pool di Thread risulta complesso e oneroso in termine di carico
di CPU e quindi se non espressamente richiesto ne faremo volentieri a meno.
A nostro aiuto viene la classe ThreadPool che ci consente di creare Thread
tramite l’ausilio del metodo QueueUserWorkItem. Tutti i Thread creati da un
ThreadPool vengono avviati con Priority di Background.
Imports System.Threading
Module Esempio08
Sub Main()
Dim i As Integer
For i = 0 To 10
ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf Worker), i)
Next
Console.WriteLine("Main id : " & _
AppDomain.GetCurrentThreadId())
Thread.Sleep(5000)
End Sub
Public Sub Worker(ByVal IndexThread As Object)
Console.WriteLine("Thread N° " & IndexThread & " id : " & _
AppDomain.GetCurrentThreadId())
End Sub
End Module
In questo esempio vengono creati tramite la classe ThreadPool dei thread di
tipo Worker che verranno eseguiti parallelamente.
Il risultato prodotto sarà il seguente.
Main id : 2008
Thread N° 0 id : 532
Thread N° 1 id : 532
Thread N° 2 id : 532
Thread N° 3 id : 532
Thread N° 4 id : 532
Thread N° 5 id : 532
Thread N° 6 id : 532
Thread N° 7 id : 532
Thread N° 8 id : 532
Thread N° 9 id : 532
Thread N° 10 id : 532
La cosa che ci viene subito all’occhio è che l’ID dei thread lanciati sono sempre gli
stessi.
Questo perché appena verrà accodato (i = 0) al ThreadPool una nuova
richiesta, il ThreadPool creerà un nuovo Thread e lo eseguirà. Se in un tempo
relativamente breve il Thread verrà terminato, il ThreadPool riutilizzerà lo
stesso Thread per l’elaborazione della coda successiva (i=1 , i=2, i=3. i=4 ecc..)
Quindi l’ID dei Thread processati sarà sempre lo stesso in quanto verrà
riutilizzata la stessa allocazione di memoria e quindi non verrà effettuato un vero
parallelismo di applicazione.
Ovviamente assumere che il termine dei Thread avvenga in modo lineare è
estremamente pericoloso (N°1, N°2, N°3, N°4, N°5, N°6, N°7, N°8, N°9, N°10)
Il codice successivo invece mostra un vero parallelismo applicativo in quanto il
termine dei Thread creati dal ThreadPool avrà un tempo di durata maggiore
del tempo minimo di esecuzione di un Thread.
Questo permetterà al ThreadPool di creare Thread con ID differenti e farli
lavorare in parallelo.
L’istruzione Thread.Sleep(New Random().Next(2000)) ci permette di
simulare una durata casuale della funzione Worker.
Imports System.Threading
Module Esempio09
Sub Main()
Dim i As Integer
For i = 0 To 10
ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf Worker), i)
Next
Console.WriteLine("Main id : " & AppDomain.GetCurrentThreadId())
Thread.Sleep(5000)
End Sub
Public Sub Worker(ByVal IndexThread As Object)
Thread.Sleep(New Random().Next(2000))
Console.WriteLine("Thread N° " & IndexThread & " id : " & _
AppDomain.GetCurrentThreadId())
End Sub
End Module
Il risultato prodotto sarà il seguente.
Thread N° 1 id : 3356
Thread N° 0 id : 3644
Thread N° 4 id : 3644
Thread N° 7 id : 1256
Thread N° 2 id : 540
Thread N° 3 id : 3356
Thread N° 6 id : 3644
Thread N° 9 id : 540
Thread N° 10 id : 3356
Thread N° 5 id : 2968
Thread N° 8 id : 1256
Ovviamente alcuni Thread potranno essere riutilizzati dal ThreadPool e quindi
manterranno ID analoghi.
Il ThreadPool ha dei metodi che possono essere invocati per facilitare il suo
utilizzo.
Di default il numero massimo di esecuzione parallela è impostato a 25 Thread
che tramite il metodo SetMaxThreads può essere variato.
Dim MaxThread As Boolean = ThreadPool.SetMaxThreads(100, 1)
restituisce un valore di tipo Boleano se la richiesta è andata a buon fine.
Tale metodo prende in input due valori, Workerthreads che definisce il numero
massimo di thread presenti nel Pool e completionPortThreads che prende in
input un numero corrispondente alle CPU sulle quali si vuole far girare il Pool.
Ovviamente una volta terminato il numero di Thread massimi in esecuzione nel
Pool, le richieste rimarranno in sospeso fino al liberarsi di un Thread
appartenente al Pool.
L’esempio successivo invece utilizza il metodo SetMaxThreads e
GetAvailableThreads che ritorna il numero dei thread disponibili all’interno del
Pool. Tale valore è la sottrazione tra il numero massimo dei Thread impostati
tramite SetMaxThreads e il numero dei thread in esecuzione.
Imports System.Threading
Module Esempio10
Sub Main()
Dim i As Integer
Dim MaxThread As Boolean = ThreadPool.SetMaxThreads(10, 1)
Console.WriteLine("SetMaxThreads : " & MaxThread)
For i = 0 To 10
Dim ReturnAddTopool As Boolean = _
ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf Worker), i)
Next
Thread.Sleep(5000)
End Sub
Public Sub Worker(ByVal IndexThread As Object)
Thread.Sleep(New Random().Next(2000))
Dim WorkerThreads As Integer
Dim ThreadId As Integer = AppDomain.GetCurrentThreadId()
Dim CompletionPortThreads As Integer
ThreadPool.GetAvailableThreads(WorkerThreads, _
CompletionPortThreads)
Console.WriteLine("Thread Available is : " & _
WorkerThreads)
Console.WriteLine("Thread N° " & IndexThread & " id : " & _
ThreadId)
End Sub
End Module
Il risultato sarà il seguente.
SetMaxThreads : True
Thread Available is : 9
Thread N° 0 id : 2064
Thread Available is : 9
Thread N° 1 id : 2064
Thread Available is : 7
Thread N° 3 id : 3784
Thread Available is : 6
Thread N° 5 id : 3784
Thread Available is : 6
Thread N° 2 id : 2064
Thread Available is : 5
Thread N° 9 id : 3596
Thread Available is : 5
Thread N° 8 id : 2064
Thread Available is : 6
Thread N° 4 id : 3320
Thread Available is : 7
Thread N° 7 id : 3784
Thread Available is : 8
Thread N° 6 id : 344
Thread Available is : 9
Thread N° 10 id : 3596
Come vediamo Thread Available è variabile in base a diversi fattori, la cosa che
salta subito all’occhio e che ci saremmo aspettati dalle print del 2 thread che i
Thread disponibili siano minori di 9. Come visto in precedenza il ThreadPool
definisce un tempo entro il quale allocare una seconda risorsa per attivare un
nuovo Thread.
Se la precedente richiesta finisce entro un tempo relativamente breve riutilizza la
stessa (e per questo mantiene anche lo stesso ID di processo).
Gli eventi
Fino ad ora abbiamo creato un Thread passandogli un indirizzo di una Sub
Routine dichiarata all’interno della funzione Main(), come detto in precedenza è
possibile istanziare una classe, passargli dei parametri e poi eseguire in un
thread separato una Routine presente di quella classe.
Nell’esempio successivo viene dichiarata una classe che al suo interno dispone di
un evento TerminateThread che verrà generato per segnalare al Main() il
completamento delle operazioni.
Imports System.Threading
Module Esempio11
Public WithEvents _Worker As Worker
Sub Main()
_Worker = New Worker
_Worker.TimeSleep = 2000
Dim _ThreadStart As New ThreadStart(AddressOf _Worker.Start)
Dim _Thread As New Thread(_ThreadStart)
_Thread.Start()
Console.WriteLine("Main End")
End Sub
Private Sub _Worker_TerminateThread(ByVal Result As String) _
Handles _Worker.TerminateThread
Console.WriteLine(Result)
End Sub
End Module
Public Class Worker
Public Event TerminateThread(ByVal Result As String)
Public TimeSleep As Integer
Public Sub Start()
Thread.Sleep(TimeSleep)
RaiseEvent TerminateThread("Worker Completing. TimeSleep is : " & _
TimeSleep)
End Sub
End Class
I risultato dell’elaborazione è il seguente
Main End
Worker Completing. TimeSleep is : 2000
Questo meccanismo di segnali ci permetterà di avere un controllo preciso su ciò
che sta succedendo all’interno del Thread.
Ma è possibile anche mettere in attesa un Thread fino a che non si verifichi una
condizione per cui far continuare l’elaborazione senza utilizzare i comandi di
sincronizzazione Suspend e poi il relativo Resume.
Nel codice successivo utilizzeremo il comando AutoResetEvent che ci permette
di utilizzare i metodi WaitOne() e Set() con cui mettere in attesa un Thread
(WaitOne) e poi successivamente dal Main far continuare l’elaborazione (Set).
Oltre a WaitOne() è possibile mettere in attesa il Thread tramite WaitAny() e
WaitAll().
WaitAny() mette in attesa il Thread fino a quando un evento venga segnalato.
WaitAll() invece mette in attesa il Thread fino a che tutti gli eventi vengano
segnalati.
Ovviamente l’evento viene segnalato quando viene evocato il metodo Set().
Questo genere di meccanismi ci permettono di controllare perfettamente il codice
che viene processato all’interno dei Thread.
Imports System.Threading
Module Esempio12
Public _AutoResetEvent As AutoResetEvent
Sub Main()
_AutoResetEvent = New AutoResetEvent(False)
Dim _ThreadStart As New ThreadStart(AddressOf Worker)
Dim _Thread As New Thread(_ThreadStart)
Console.WriteLine("Main : Start Thread")
_Thread.Start()
Console.WriteLine("Main Slepp 1 Second...")
Thread.Sleep(1000)
Console.WriteLine("Main Signal Worker Thread")
_AutoResetEvent.Set()
End Sub
Public Sub Worker()
Console.WriteLine("Worker thread Active")
_AutoResetEvent.WaitOne()
Console.WriteLine("Worker thread Completing")
End Sub
End Module
I risultato dell’elaborazione è il seguente
Main : Start Thread
Worker thread Active
Main Slepp 1 Second...
Main Signal Worker Thread
Worker thread Completing
I Timer
Il nameSpace System.Threading comprende un oggetto molto utile che
andremo ad analizzare che si chiama Timer.
Il timer viene in nostro aiuto quando occorre far eseguire porzioni di codice a
tempo.
Nell’esempio successivo viene mostrato l’utilizzo di un timer, da tenere in
considerazione che questo tipo di classe può essere utilizzata anche all’interno
dei Windows Service quando non è possibile utilizzare la classe Timer presente
nelle Windows Form.
Il costruttore della classe Timer prende in input un oggetto di tipo
TimerCallBack che identifica la locazione di memoria da andare ad eseguire, lo
State, un oggetto contenente i valori da passare al metodo da eseguire,
dueTime che specifica il tempo che verrà calcolato prima della prima chiamata
(0 vuol dire subito) e infine il Period che corrisponde al tempo in millisecondi tra
una chiamata e l’altra.
Imports System.Threading
Module Esempio13
Sub Main()
Dim _TimerCallback As New TimerCallback(AddressOf Worker)
Dim _Timer As Timer = New Timer(_TimerCallback, _
Nothing, _
0, _
1000)
Thread.Sleep(6000)
End Sub
Public Sub Worker(ByVal State As Object)
Console.WriteLine("Worker Now : " & DateTime.Now.ToLongTimeString())
End Sub
End Module
Il risultato sarà il seguente.
Worker Now : 9.27.43
Worker Now : 9.27.44
Worker Now : 9.27.45
Worker Now : 9.27.46
Worker Now : 9.27.47
Worker Now : 9.27.48
Worker Now : 9.27.49
Apartment Thread e interoperabilità COM
In ultima analisi vediamo in che maniera far collaborare i Thread .NET con il
vecchio mondo COM. Per questo motivo avremo senz’altro notato delle proprietà
presenti all’interno dell’oggetto Thread denominate SetApartmentState() e
GetApartmentState().
L’Apartment State è un contenitore logico presente in un processo dove sono
allocati oggetti che condividono gli stessi requisiti di accesso ad un Thread.
Tele specifica esiste nella vecchia COM ma non è presente all’interno del
Common Language Runtime di .NET. I Componenti COM grazie all’Apartment
garantivano una corretta sincronizzazione delle risorse e per questa loro logica di
implementazione che occorre modificare i nostri Thread se vogliamo far uso di
componenti COM dal loro interno.
La proprietà SetApartmentState() prende in input i valori dati
dall’enumerazione riportata in seguito per comodità.
Public Enum ApartmentState
STA = 0 'Single thread Apartment
MTA = 1 'MultiThread Apartment
unknow = 2 'Non definito
End Enum
Se non viene dichiarato un ApartmentState() lo stato prelevato dalla proprietà
GetApartmentState() sarà impostato su unknow. l’oggetto COM se noterà un
Apartment incompatibile utilizzerà un proxy per farlo lavorare correttamente.
Per migliorare le performance di comunicazione tra il thread chiamante e
l’oggetto COM è meglio specificare il corretto stato di Apartment da utilizzare.
Non è possibile modificare una volta avviato il Thread lo stato di Apartment ne
annullarne l’inizzializzazione.
Imports System.Threading
Imports ComDemo
Module Esempio14
Sub Main()
Dim _ThreadStart As New ThreadStart(AddressOf Worker)
Dim _Thread As New Thread(_ThreadStart)
Try
Console.WriteLine("Worker ApartmentState : " & _
_Thread.GetApartmentState.ToString)
_Thread.TrySetApartmentState(ApartmentState.STA)
Console.WriteLine("Worker ApartmentState : " & _
_Thread.GetApartmentState.ToString)
_Thread.Start()
Catch ex As ThreadStateException
Console.WriteLine("ThreadStateException occurs " & ex.Message)
End Try
_Thread.Join()
End Sub
Sub Worker()
Dim ComObject As New ComDemo.Class1
Console.WriteLine("Worker invoke SetValue")
ComObject.SetValue("Inizialize")
Thread.Sleep(2000)
Dim ComValue As String = ComObject.GetValue
Console.WriteLine("Worker invoke GetValue : " & ComValue)
End Sub
End Module
Il risultato sarà il seguente.
Worker ApartmentState : Unknown
Worker ApartmentState : MTA
Worker invoke SetValue
Worker invoke GetValue : Inizialize
Questo esempio fa vedere che è possible impostare l’ApartmentState
solamente in fase di creazione del thread prima di aver invocato il metodo
Start().
Conclusioni
Il MultiThreading in Microsoft .NET è possibile ed è di semplice utilizzo come
abbiamo appreso in questo documento.
Le tecniche di Pool di thread già presenti all’interno del Framework facilitano di
gran lunga la realizzazione e la progettazione di applicativi scalabili e performanti
che fino ad ora erano onerosi e complessi da realizzare.
Al programmatore è rivolta la massima accortezza nello scrivere le applicazioni
che con l’avvento del MultiThreading introducono meccanismi complessi di
tracciamento dei processi e di Locking.
Bibliografia

More Related Content

Similar to Multithread Programming

Sviluppare per microsoft band
Sviluppare per microsoft bandSviluppare per microsoft band
Sviluppare per microsoft bandDotNetCampus
 
SVILUPPARE PER MICROSOFT BAND
SVILUPPARE PER MICROSOFT BANDSVILUPPARE PER MICROSOFT BAND
SVILUPPARE PER MICROSOFT BANDDotNetCampus
 
Progettazione e sviluppo di un software applicativo su un single board computer
Progettazione e sviluppo di un software applicativo su un single board computerProgettazione e sviluppo di un software applicativo su un single board computer
Progettazione e sviluppo di un software applicativo su un single board computerAlessandro Mascherin
 
Costruire un client .NET per SugarCRM
Costruire un client .NET per SugarCRMCostruire un client .NET per SugarCRM
Costruire un client .NET per SugarCRMAntonio Musarra
 
Progetto SOD Davide Sito
Progetto SOD Davide SitoProgetto SOD Davide Sito
Progetto SOD Davide SitoDavide Sito
 
Deploy MongoDB su Infrastruttura Amazon Web Services
Deploy MongoDB su Infrastruttura Amazon Web ServicesDeploy MongoDB su Infrastruttura Amazon Web Services
Deploy MongoDB su Infrastruttura Amazon Web ServicesStefano Dindo
 
How create a single page apps using html5 and javascript
How create a single page apps using html5 and javascript How create a single page apps using html5 and javascript
How create a single page apps using html5 and javascript Stefano Marchisio
 
What's New in ASP.NET 4.5 and Visual Studio 2012
What's New in ASP.NET 4.5 and Visual Studio 2012What's New in ASP.NET 4.5 and Visual Studio 2012
What's New in ASP.NET 4.5 and Visual Studio 2012Andrea Dottor
 
ASP.NET MVC 6 - uno sguardo al futuro
ASP.NET MVC 6 - uno sguardo al futuroASP.NET MVC 6 - uno sguardo al futuro
ASP.NET MVC 6 - uno sguardo al futuroAndrea Dottor
 
AreaMVC: un'architettura software basata sulla semplicità
AreaMVC: un'architettura software basata sulla semplicitàAreaMVC: un'architettura software basata sulla semplicità
AreaMVC: un'architettura software basata sulla semplicitàGiulio Destri
 
Meetup DotNetCode Settembre 2018 - ASP.NET Core 2.1
Meetup DotNetCode Settembre 2018 - ASP.NET Core 2.1Meetup DotNetCode Settembre 2018 - ASP.NET Core 2.1
Meetup DotNetCode Settembre 2018 - ASP.NET Core 2.1dotnetcode
 
ASP.NET performance optimization
ASP.NET performance optimizationASP.NET performance optimization
ASP.NET performance optimizationAndrea Dottor
 
Sviluppare per Microsoft Band
Sviluppare per Microsoft BandSviluppare per Microsoft Band
Sviluppare per Microsoft BandMassimo Bonanni
 
Sistemi Context-aware: Esercitazione 3
Sistemi Context-aware: Esercitazione 3Sistemi Context-aware: Esercitazione 3
Sistemi Context-aware: Esercitazione 3Marco Loregian
 
Introduzione ad angular 7/8
Introduzione ad angular 7/8Introduzione ad angular 7/8
Introduzione ad angular 7/8Valerio Radice
 
Gam04 introduzione a-netduino_final
Gam04   introduzione a-netduino_finalGam04   introduzione a-netduino_final
Gam04 introduzione a-netduino_finalDotNetCampus
 
MITM Attack with Patching Binaries on the Fly by Adding Shellcodes
MITM Attack with Patching Binaries on the Fly by Adding ShellcodesMITM Attack with Patching Binaries on the Fly by Adding Shellcodes
MITM Attack with Patching Binaries on the Fly by Adding ShellcodesGianluca Gabrielli
 
Windows azure - abbattere tempi e costi di sviluppo
Windows azure - abbattere tempi e costi di sviluppoWindows azure - abbattere tempi e costi di sviluppo
Windows azure - abbattere tempi e costi di sviluppoAndrea Dottor
 

Similar to Multithread Programming (20)

Sviluppare per microsoft band
Sviluppare per microsoft bandSviluppare per microsoft band
Sviluppare per microsoft band
 
SVILUPPARE PER MICROSOFT BAND
SVILUPPARE PER MICROSOFT BANDSVILUPPARE PER MICROSOFT BAND
SVILUPPARE PER MICROSOFT BAND
 
Progettazione e sviluppo di un software applicativo su un single board computer
Progettazione e sviluppo di un software applicativo su un single board computerProgettazione e sviluppo di un software applicativo su un single board computer
Progettazione e sviluppo di un software applicativo su un single board computer
 
Programming iOS lezione 4
Programming iOS lezione 4Programming iOS lezione 4
Programming iOS lezione 4
 
Costruire un client .NET per SugarCRM
Costruire un client .NET per SugarCRMCostruire un client .NET per SugarCRM
Costruire un client .NET per SugarCRM
 
Progetto SOD Davide Sito
Progetto SOD Davide SitoProgetto SOD Davide Sito
Progetto SOD Davide Sito
 
Deploy MongoDB su Infrastruttura Amazon Web Services
Deploy MongoDB su Infrastruttura Amazon Web ServicesDeploy MongoDB su Infrastruttura Amazon Web Services
Deploy MongoDB su Infrastruttura Amazon Web Services
 
How create a single page apps using html5 and javascript
How create a single page apps using html5 and javascript How create a single page apps using html5 and javascript
How create a single page apps using html5 and javascript
 
What's New in ASP.NET 4.5 and Visual Studio 2012
What's New in ASP.NET 4.5 and Visual Studio 2012What's New in ASP.NET 4.5 and Visual Studio 2012
What's New in ASP.NET 4.5 and Visual Studio 2012
 
ASP.NET MVC 6 - uno sguardo al futuro
ASP.NET MVC 6 - uno sguardo al futuroASP.NET MVC 6 - uno sguardo al futuro
ASP.NET MVC 6 - uno sguardo al futuro
 
AreaMVC: un'architettura software basata sulla semplicità
AreaMVC: un'architettura software basata sulla semplicitàAreaMVC: un'architettura software basata sulla semplicità
AreaMVC: un'architettura software basata sulla semplicità
 
Meetup DotNetCode Settembre 2018 - ASP.NET Core 2.1
Meetup DotNetCode Settembre 2018 - ASP.NET Core 2.1Meetup DotNetCode Settembre 2018 - ASP.NET Core 2.1
Meetup DotNetCode Settembre 2018 - ASP.NET Core 2.1
 
ASP.NET performance optimization
ASP.NET performance optimizationASP.NET performance optimization
ASP.NET performance optimization
 
Sviluppare per Microsoft Band
Sviluppare per Microsoft BandSviluppare per Microsoft Band
Sviluppare per Microsoft Band
 
Sistemi Context-aware: Esercitazione 3
Sistemi Context-aware: Esercitazione 3Sistemi Context-aware: Esercitazione 3
Sistemi Context-aware: Esercitazione 3
 
Introduzione ad angular 7/8
Introduzione ad angular 7/8Introduzione ad angular 7/8
Introduzione ad angular 7/8
 
Gam04 introduzione a-netduino_final
Gam04   introduzione a-netduino_finalGam04   introduzione a-netduino_final
Gam04 introduzione a-netduino_final
 
MITM Attack with Patching Binaries on the Fly by Adding Shellcodes
MITM Attack with Patching Binaries on the Fly by Adding ShellcodesMITM Attack with Patching Binaries on the Fly by Adding Shellcodes
MITM Attack with Patching Binaries on the Fly by Adding Shellcodes
 
Windows azure - abbattere tempi e costi di sviluppo
Windows azure - abbattere tempi e costi di sviluppoWindows azure - abbattere tempi e costi di sviluppo
Windows azure - abbattere tempi e costi di sviluppo
 
Java lezione 9
Java lezione 9Java lezione 9
Java lezione 9
 

Multithread Programming

  • 1. Scrivere Applicazioni Multi Threading in .NET Di Massimiliano Brolli
  • 2. Premessa...................................................................................................3 Prerequisiti ................................................................................................3 Il Multi Threading .......................................................................................4 Le basi ......................................................................................................5 Il Namespace ..........................................................................................5 Le Dichiarazioni .......................................................................................5 Lanciare un thread ...................................................................................5 Meccanismi di Sincronizzazione ....................................................................6 Sleep......................................................................................................6 Start ......................................................................................................7 ResetAbort..............................................................................................7 Suspend .................................................................................................7 Resume ..................................................................................................7 Join........................................................................................................8 Interrupt.................................................................................................8 Abort......................................................................................................9 SpinWait............................................................................................... 10 Stato dei thread ....................................................................................... 11 La priorità dei thread..............................................................................13 Scopo delle variabili ...............................................................................15 Pool di Thread .......................................................................................... 18 Gli eventi................................................................................................. 22 I Timer.................................................................................................... 24 Apartment Thread e interoperabilità COM .................................................... 25 Conclusioni .............................................................................................. 27 Bibliografia .............................................................................................. 28
  • 3. Premessa Molto spesso ci siamo imbattuti nel termine AJAX, acronimo di Asynchronous JavaScript and XML ovvero un sistema che permette alle pagine web di agire in maniera asincrona. Lo scopo di questo documento e far capire tramite semplici esempi le componenti/funzionalità offerte da questo straordinario Framework messo a disposizione anche in ambiente .NET. Prerequisiti Per procedere alla lettura di questo documento occorre aver letto le seguenti dispense : • Introduzione agli algoritmi. • La scelta tra Microsoft.NET & Java • Predisposizione Virtual Machine di base • Predisposizione Virtual Machine per l'ambiente di sviluppo Microsoft .NET • Introduzione alla scrittura del codice .NET • Scrivere applicazioni .NET con Visual Studio • Panoramica sulla programmazione ad oggetti • La Storia • Introduzione al Framework .NET • Gli oggetti ADO.NET • Predisposizione Ambiente SQL Server • Applicazioni Windows Forms • ADO.NET Entity Framework • Scrivere ASP.NET Web Application, premessa e predisposizione • Scrivere ASP.NET Web Application, La prima applicazione Web Forms • Scrivere ASP.NET Web Application, I Web Services • Scrivere ASP.NET Web Application, Applicazioni MVC • Scrivere ASP.NET Web Application, AJAX
  • 4. Il Multi Threading Il MultiThreading comparso con Windows NT 3.1 e successivamente con Windows 95 rappresenta una rivoluzione nel campo informatico di largo consumo. Solamente i linguaggi di basso livello prima dell’evento del Framework .NET potevano realizzare applicazioni MultiThreading come ad esempio MSVC++ con l’ausilio di particolari librerie. In questo documento andremo ad analizzare le istruzioni che occorre imparare per lavorare con i Thread utilizzando Microsoft.NET Framework 2.0 ed il linguaggio VB.NET. Un Thread o filo in inglese, rappresenta un processo che può lavorare parallelamente al padre chiamante. Nel framework.NET si può far lavorare in un Thread separato porzioni di codice quali Sub Routine o intere classi per realizzare applicazioni performanti che hanno come limite solamente la fantasia. Addentrandoci nel documento cominceremo a familiarizzare con i meccanismi di programmazione Asincrona che introdurranno problematiche nuove quali la concorrenza applicativa e i meccanismi di sincronizzazione.
  • 5. Le basi Il Namespace Per realizzare applicazioni MultiThreading occorre utilizzare il nameSpace System.Threading come viene mostrato nell’esempio successivo. Imports System.Threading Le Dichiarazioni Per lavorare con i Thread occorre dichiarare un oggetto di tipo ThreadStart che permette di eseguire lo Start di un Thread e un oggetto Thread che ci permetterà di crearlo. Private _ThreadStart As ThreadStart Private _Thread As Thread Lanciare un thread Come detto in precedenza è possibile far lavorare su un thread separato porzioni di codice appartenenti ad una Sub Routine oppure una intera classe. Nell’esempio successivo viene lanciato in un Thread separato una Sub Routine ClientGoSend. L’istruzione _ClientThread.Start permetterà di lanciare il Thread come viene mostrato nell’esempio successivo. _ThreadStart = New ThreadStart(AddressOf ClientGoSend) _Thread = New Thread(_ThreadStart) _Thread.Start Public sub ClientGoSend End sub
  • 6. Meccanismi di Sincronizzazione I Thread essendo delle entità separate rispetto al processo chiamante che li ha creati hanno una loro vita, questo vuol dire che mentre il padre effettuerà ad esempio un loop infinito il Thread creato in precedenza prosegue in parallelo la propria attività. Questo meccanismo introduce alla programmazione Asincrona che seppur utile aggiunge delle problematiche di sincronismo alle nostre applicazioni. Il .NET Framework ci mette a disposizione dei metodi nella classe thread che permettono di attendere per un determinato tempo un qualche evento oppure ripartire nell’elaborazione allo scatenare di un altro. Sleep L’istruzione Thread.Sleep(100) mette in attesa il thread per un valore in Millisecondi. _ThreadStart = New ThreadStart(AddressOf Send) _Thread = New Thread(_ThreadStart) _Thread.Start Console.WriteLine(“Main” & AppDomain.GetCurrentThreadId())‘del thread Main Public sub Send Dim objThread As Thread = Thread.CurrentThread() objThread.Sleep(100) ‘Mette in attesa il thread per 100 Millisecondi Console.WriteLine(“Thread” & AppDomain.GetCurrentThreadId()) ‘ID del thread objThread.Abort ‘Distrugge il Thread End sub L’istruzione Console.WriteLine(AppDomain.GetCurrentThreadId()) viene ripetuta per due volte all’interno dell’applicazione perché stamperà a console prima l’ID del processo chiamante e poi l’ID del processo chiamato. Il risultato sarà Main 2574 Thread 2575 Oppure Thread 2575 Main 2574 Questo perchè potrà eseguire l’istruzione Console prima il Main oppure prima il Thread creato. Di seguito vengono riportati dei metodi della classe thread che servono per la sincronizzazione dei Thread.
  • 7. Start L’istruzione Start lancia un thread. ResetAbort L’istruzione ResetAbort interrompe la distruzione di un thread. Suspend L’istruzione Suspend mette in attesa il thread. Resume L’istruzione Resume riattiva il thread.
  • 8. Join L’istruzione Join mette in attesa il thread. Nell’esempio sotto riportato, il codice Main chiama un thread che terminirà allo scadere di 4 secondi e scriverà “Worker End”. Imports System.Threading Module Esempio01 Private _ThreadStart As ThreadStart Private _Thread As Thread Public Sub Worker() Thread.Sleep(4000) Console.WriteLine("Worker End") End Sub Sub Main() _ThreadStart = New ThreadStart(AddressOf Worker) _Thread = New Thread(_ThreadStart) _Thread.Start() _Thread.Join() 'Attende che il thread sia completato Console.WriteLine("Main End") End Sub End Module Eseguendo il codice vedremo che il risultato sarà il seguente Worker End Main End Questo perché il main attenderà il completamento del thread _Thread prima di effettuare la scrittura di “Main End” grazie al metodo Join. Se asterischiamo l’istruzione _Thread.Join() e rieseguiamo l’esempio noteremo che il risultato sarà il seguente. Main End Worker End Questo perché il Main non attenderà la conclusione del Thread _Thread che scriverà dopo 4 secondo il messaggio “Worker End” Il metodo Join può prendere in input un parametro espresso in millisecondi/Timestamp. Tali millisecondi/Timestamp stanno ad indicare il tempo massimo che verrà atteso per la terminazione del Thread. Interrupt L’istruzione Interrupt interrompe lo stato di attesa di un thread.
  • 9. Abort L’istruzione Abort distrugge il thread. E’ da notare che tale istruzione non è una garanzia di distruzione in quanto genera al suo interno un’eccezione di tipo ThreadAbortException e imposta lo stato su AbortRequested. Se all’interno di questa eccezione venisse invocato il metodo ResetAbort verrebbe vanificata la chiamata di Abort precedentemente invocata e quindi è importante fare attenzione al codice che stiamo implementando tenendo conto di questi meccanismi di concorrenza. La stessa cosa vale per il metodo Interrupt che a sua volta genera un’eccezione di tipo ThreadInterruptedException e imposta lo stato su Running anche se interromperà l’esecuzione. Il codice seguente mostra il comportamento di quanto descritto sopra. Imports System.Threading Module Esempio02 Private _ThreadStart As ThreadStart Private _Thread As Thread Sub Main() _ThreadStart = New ThreadStart(AddressOf Worker) _Thread = New Thread(_ThreadStart) _Thread.Start() _Thread.Abort() 'Da provare cambiandolo in Interrupt Console.WriteLine("Main End") End Sub Public Sub Worker() Dim objThread As Thread = Thread.CurrentThread() Try Console.WriteLine("Worker Sleeping") Thread.Sleep(4000) 'generata se invocato il metodo interrupt Catch e As ThreadInterruptedException Console.WriteLine("Worker interrupt : ThreadState " & _ Thread.CurrentThread.ThreadState.ToString) 'generata se invocato il metodo Abort Catch e As ThreadAbortException Console.WriteLine("Worker Abort : ThreadState " & _ Thread.CurrentThread.ThreadState.ToString) 'operazioni conclusive del costrutto Try/Catch Finally Console.WriteLine("Worker End : ThreadState " & _ Thread.CurrentThread.ThreadState.ToString) End Try Console.WriteLine("Worker Completed") End Sub
  • 10. End Module Eseguendolo il risultato sarà il seguente Worker Sleeping Worker Abort : ThreadState AbortRequested Worker End : ThreadState AbortRequested Main End Da notare che il thread non è stato completato ( non viene notificato “Worker Completed”) in quanto è stata richiesta una operazione di Abort che viene anche notificata dall’istruzione Thread.CurrentThread.ThreadState.ToString che ci mostra lo stato del thread impostato su AbortRequested. Se al posto di _ClientThread.Abort() inserissimo _ClientThread.Interrupt() nel main del modulo il risultato cambierà in questo modo. Worker Sleeping Main End Worker interrupt : ThreadState Running Worker End : ThreadState Running Worker Completed Da notare che il motodo Interrupt ha interrotto l’esecuzione del thread senza distruggerlo, infatti in questo esempio viene notificata la terminazione del thread dal messaggio “Worker Completed” che ci avverte che l’invocazione di interrupt ha interrotto lo stato di Sleep del thread. SpinWait L’istruzione SpinWait viene utilizzata su applicazione che girano su macchine multiprocessore e costituisce un’alternativa al comando Sleep che restituisce cicli di CPU rimanendo in attesa.
  • 11. Stato dei thread I Thread possono assumere diversi stati in funzione della loro sincronizzazione con i processi chiamanti. In questa sezione andremo ad analizzare i diversi stati per capirne meglio il loro utilizzo. Come visto in precedenza, per acquisire lo stato di un thread occorre accedere al metodo Shared Thread.CurrentThread.ThreadState.ToString che ci ritorna un valore in formato Stringa dello stato corrente del thread. Running La parola stessa ci dice che il thread sta in esecuzione Imports System.Threading Module Esempio03 Sub Main() Dim _ThreadStart As New ThreadStart(AddressOf Worker) Dim _Thread As New Thread(_ThreadStart) _Thread.Start() While True Thread.Sleep(1000) Console.WriteLine("Worker End : ThreadState " & _ _Thread.ThreadState.ToString) End While Console.WriteLine("Main End") End Sub Public Sub Worker() While True End While Console.WriteLine("Worker Completed") End Sub End Module WaitSleepJoin Questo stato ci comunica che il thread si trova in uno stato di Wait, Sleep o Join. Imports System.Threading Module Esempio04 Sub Main() Dim _ThreadStart As New ThreadStart(AddressOf Worker) Dim _Thread As New Thread(_ThreadStart) _Thread.Start() While True Thread.Sleep(1000) Console.WriteLine("Worker End : ThreadState " & _ _Thread.ThreadState.ToString) End While Console.WriteLine("Main End")
  • 12. End Sub Public Sub Worker() While True Thread.Sleep(1000) End While Console.WriteLine("Worker Completed") End Sub End Module In questo caso è lo stesso Thread che sta effettuando un ciclo While true ogni un secondo. AbortRequested La parola stessa ci dice che per il thread è stata invocata una Abort. Per il metodo Abort vi rimando all’esempio del metodo Abort nella sezione Meccanismi di Sincronizzazione
  • 13. La priorità dei thread In un ambiente Multi Threading è possibile impostare una priorità di esecuzione per ogni singolo thread. Tale meccanismo permette di condividere nel migliore dei modi i cicli di CPU della macchina permettendo ai processi critici di avere una maggiore priorità in termine di esecuzione sui processi secondari o di minor importanza applicativa. I processi Microsoft .NET sono di due tipi, processi di Foreground e processi di Background. Ai processi di Foreground possiamo attribuire una priorità di esecuzione in base ad una Enumerazione contenuta nella classe System.threading.ThreadPriority qua sotto ricreata per maggiore chiarezza. ‘Enumeration che riporta le priorità per i thread di Foreground Public Enum Priority Lowest = 0 BelowNormal = 1 Normal = 2 AboveNormal = 3 Highest = 4 End Enum Nell’esempio successivo abbiamo utilizzato il metodo Priority per assegnare una priorità di esecuzione. E’ importante dire che un Thread creato dal Runtime senza specifica di priorità viene creato come Foreground di tipo Normal. Imports System.Threading Module Esempio05 Sub Main() Dim _ThreadStart As New ThreadStart(AddressOf Worker) Dim _Thread As New Thread(_ThreadStart) _Thread.Start() _Thread.Priority = ThreadPriority.Lowest Console.WriteLine("Main End") End Sub Public Sub Worker() Thread.Sleep(4000) Console.WriteLine("Worker Completed, Priority is : " & _ Thread.CurrentThread.Priority.ToString) End Sub End Module Il risultato di questo esempio ritornerà il seguente Output.
  • 14. Worker Completed, Priority is : Lowest Main End Cambiando il tipo di Priorità da Lowest a Highest noteremo che l’applicazione attenderà sempre la conclusione del thread prima di essere terminata (Ovviamente omettendo il metodo Join). Questo perché si tratta di un thread di tipo Foreground e quindi deve essere per forza terminato prima di terminare l’intero processo. E’ possibile definire un Thread come processo di Background utilizzando la proprietà Thread.IsBackground di tipo Boleano. Impostando a True questo valore il processo potrà essere terminato in qualsiasi momento, talei processi di background saranno processi non di vitale importanza per le nostre applicazioni. Imports System.Threading Module Esempio06 Sub Main() Dim _ThreadStart As New ThreadStart(AddressOf Worker) Dim _Thread As New Thread(_ThreadStart) _Thread.Start() _Thread.IsBackground = True Console.WriteLine("Main End") End Sub Public Sub Worker() Thread.Sleep(4000) Console.WriteLine("Worker Completed, Priority is : " & _ Thread.CurrentThread.Priority.ToString) End Sub End Module Il risultato di questo esempio ritornerà il seguente Output. Main End Questo perchè il processo figlio è stato definito di Background e quindi è stato terminato senza preavviso dal processo chiamante.
  • 15. Scopo delle variabili E’ possibile creare delle variabili che avranno uno scopo limitato al processo che le sta eseguendo. Nell’esempio che viene riportato in seguito, la variabile di classe Value viene dichiarata inserendo il suffisso <ThreadStatic()>. Questa variabile non sarà più una variabile condivisa sia dal processo chiamante che dal processo figlio, ma ogni processo avrà una copia di tale variabile e ne potrà fare un uso esclusivo. Imports System.Threading Module Esempio07 <ThreadStatic()> Public Value As String = "Main" Sub Main() Console.WriteLine("Main Value = " & Value) Dim _ThreadStart As New ThreadStart(AddressOf Worker) Dim _Thread As New Thread(_ThreadStart) _Thread.Start() _Thread.Join() Console.WriteLine("Main Value = " & Value) End Sub Public Sub Worker() Console.WriteLine("Worker Value = " & Value) Value = "Worker" Console.WriteLine("Worker Value = " & Value) End Sub End Module Il risultato di questo esempio ritornerà il seguente Output. Main Value = Main Worker Value = Worker Value = Worker Main Value = Main Main End Vediamo come la variabile Value impostata a “Main” sarà visibile solamente nel Main del programma, mentre il Thread Worker ne potrà fare un uso esclusivo impostando un valore che non verrà condiviso con il programma Main chiamante. E’ possibile utilizzando I Thread definire dei punti di codice nel quale non possono accedervi più Thread contemporaneamente. Questo è possibile grazie all’istruzione SyncLock. Nell’esempio successivo verrà definita una variabile di classe di nome Lock e nel metodo Worker verrà utilizzata l’istruzione SyncLock Lock che tramite l’oggetto globale Lock determinerà chi far accedere alla porzione di codice specifica.
  • 16. L’oggetto Lock deve essere definito di tipo Object. Imports System.Threading Module Esempio15 Public Counter As Integer Public Lock As New Object Sub Main() Dim i As Integer For i = 0 To 10 Dim _ThreadStart As New ThreadStart(AddressOf Worker) Dim _Thread As New Thread(_ThreadStart) _Thread.Start() Next End Sub Public Sub Worker() ‘SyncLock Lock Counter = Counter + 1 Thread.Sleep(New Random().Next(500, 2000)) Console.WriteLine("Worker Counter : " & Counter) ‘End SyncLock End Sub End Module Il risultato di questo esempio ritornerà il seguente Output. Questo perché la display del contatore avverrà dopo che tutti i thread avranno effettuato la somma. Worker Counter : 11 Worker Counter : 11 Worker Counter : 11 Worker Counter : 11 Worker Counter : 11 Worker Counter : 11 Worker Counter : 11 Worker Counter : 11 Worker Counter : 11 Worker Counter : 11 Worker Counter : 11 Se disasterischiamo le istruzioni SyncLock e rieseguiamo l’esempio vedremo che il risultato cambierà in questo modo. Worker Counter : 1 Worker Counter : 2 Worker Counter : 3 Worker Counter : 4 Worker Counter : 5 Worker Counter : 6 Worker Counter : 7 Worker Counter : 8 Worker Counter : 9 Worker Counter : 10 Worker Counter : 11
  • 17. Questo perché il blocco di codice ha un lock determinato dall’oggetto Lock definito come variabile di classe che ne restituisce il sincronismo. Da notare che il primo thread accederà al blocco e gli altri rimarranno in attesa fino a quando il primo thread non accederà all’istruzione End SyncLock, a quel punto il secondo entrerà in azione e così via.
  • 18. Pool di Thread I Pool di Thread vengono in nostro aiuto quando occorre creare dei processi di lunga durata che lavorano in parallelo. Un classico esempio sono quelle applicazioni Server (Web Server, SMTP Server ecc..) che non possono smaltire una richiesta alla volta ma debbono prendere in carico le richieste e processarle in parallelo. Quindi l’utilizzo di Pool di Thread risulta complesso e oneroso in termine di carico di CPU e quindi se non espressamente richiesto ne faremo volentieri a meno. A nostro aiuto viene la classe ThreadPool che ci consente di creare Thread tramite l’ausilio del metodo QueueUserWorkItem. Tutti i Thread creati da un ThreadPool vengono avviati con Priority di Background. Imports System.Threading Module Esempio08 Sub Main() Dim i As Integer For i = 0 To 10 ThreadPool.QueueUserWorkItem( _ New WaitCallback(AddressOf Worker), i) Next Console.WriteLine("Main id : " & _ AppDomain.GetCurrentThreadId()) Thread.Sleep(5000) End Sub Public Sub Worker(ByVal IndexThread As Object) Console.WriteLine("Thread N° " & IndexThread & " id : " & _ AppDomain.GetCurrentThreadId()) End Sub End Module In questo esempio vengono creati tramite la classe ThreadPool dei thread di tipo Worker che verranno eseguiti parallelamente. Il risultato prodotto sarà il seguente. Main id : 2008 Thread N° 0 id : 532 Thread N° 1 id : 532 Thread N° 2 id : 532 Thread N° 3 id : 532 Thread N° 4 id : 532 Thread N° 5 id : 532 Thread N° 6 id : 532 Thread N° 7 id : 532 Thread N° 8 id : 532 Thread N° 9 id : 532 Thread N° 10 id : 532
  • 19. La cosa che ci viene subito all’occhio è che l’ID dei thread lanciati sono sempre gli stessi. Questo perché appena verrà accodato (i = 0) al ThreadPool una nuova richiesta, il ThreadPool creerà un nuovo Thread e lo eseguirà. Se in un tempo relativamente breve il Thread verrà terminato, il ThreadPool riutilizzerà lo stesso Thread per l’elaborazione della coda successiva (i=1 , i=2, i=3. i=4 ecc..) Quindi l’ID dei Thread processati sarà sempre lo stesso in quanto verrà riutilizzata la stessa allocazione di memoria e quindi non verrà effettuato un vero parallelismo di applicazione. Ovviamente assumere che il termine dei Thread avvenga in modo lineare è estremamente pericoloso (N°1, N°2, N°3, N°4, N°5, N°6, N°7, N°8, N°9, N°10) Il codice successivo invece mostra un vero parallelismo applicativo in quanto il termine dei Thread creati dal ThreadPool avrà un tempo di durata maggiore del tempo minimo di esecuzione di un Thread. Questo permetterà al ThreadPool di creare Thread con ID differenti e farli lavorare in parallelo. L’istruzione Thread.Sleep(New Random().Next(2000)) ci permette di simulare una durata casuale della funzione Worker. Imports System.Threading Module Esempio09 Sub Main() Dim i As Integer For i = 0 To 10 ThreadPool.QueueUserWorkItem( _ New WaitCallback(AddressOf Worker), i) Next Console.WriteLine("Main id : " & AppDomain.GetCurrentThreadId()) Thread.Sleep(5000) End Sub Public Sub Worker(ByVal IndexThread As Object) Thread.Sleep(New Random().Next(2000)) Console.WriteLine("Thread N° " & IndexThread & " id : " & _ AppDomain.GetCurrentThreadId()) End Sub End Module Il risultato prodotto sarà il seguente. Thread N° 1 id : 3356 Thread N° 0 id : 3644 Thread N° 4 id : 3644 Thread N° 7 id : 1256 Thread N° 2 id : 540 Thread N° 3 id : 3356 Thread N° 6 id : 3644
  • 20. Thread N° 9 id : 540 Thread N° 10 id : 3356 Thread N° 5 id : 2968 Thread N° 8 id : 1256 Ovviamente alcuni Thread potranno essere riutilizzati dal ThreadPool e quindi manterranno ID analoghi. Il ThreadPool ha dei metodi che possono essere invocati per facilitare il suo utilizzo. Di default il numero massimo di esecuzione parallela è impostato a 25 Thread che tramite il metodo SetMaxThreads può essere variato. Dim MaxThread As Boolean = ThreadPool.SetMaxThreads(100, 1) restituisce un valore di tipo Boleano se la richiesta è andata a buon fine. Tale metodo prende in input due valori, Workerthreads che definisce il numero massimo di thread presenti nel Pool e completionPortThreads che prende in input un numero corrispondente alle CPU sulle quali si vuole far girare il Pool. Ovviamente una volta terminato il numero di Thread massimi in esecuzione nel Pool, le richieste rimarranno in sospeso fino al liberarsi di un Thread appartenente al Pool. L’esempio successivo invece utilizza il metodo SetMaxThreads e GetAvailableThreads che ritorna il numero dei thread disponibili all’interno del Pool. Tale valore è la sottrazione tra il numero massimo dei Thread impostati tramite SetMaxThreads e il numero dei thread in esecuzione. Imports System.Threading Module Esempio10 Sub Main() Dim i As Integer Dim MaxThread As Boolean = ThreadPool.SetMaxThreads(10, 1) Console.WriteLine("SetMaxThreads : " & MaxThread) For i = 0 To 10 Dim ReturnAddTopool As Boolean = _ ThreadPool.QueueUserWorkItem( _ New WaitCallback(AddressOf Worker), i) Next Thread.Sleep(5000) End Sub Public Sub Worker(ByVal IndexThread As Object) Thread.Sleep(New Random().Next(2000)) Dim WorkerThreads As Integer Dim ThreadId As Integer = AppDomain.GetCurrentThreadId() Dim CompletionPortThreads As Integer ThreadPool.GetAvailableThreads(WorkerThreads, _ CompletionPortThreads) Console.WriteLine("Thread Available is : " & _ WorkerThreads)
  • 21. Console.WriteLine("Thread N° " & IndexThread & " id : " & _ ThreadId) End Sub End Module Il risultato sarà il seguente. SetMaxThreads : True Thread Available is : 9 Thread N° 0 id : 2064 Thread Available is : 9 Thread N° 1 id : 2064 Thread Available is : 7 Thread N° 3 id : 3784 Thread Available is : 6 Thread N° 5 id : 3784 Thread Available is : 6 Thread N° 2 id : 2064 Thread Available is : 5 Thread N° 9 id : 3596 Thread Available is : 5 Thread N° 8 id : 2064 Thread Available is : 6 Thread N° 4 id : 3320 Thread Available is : 7 Thread N° 7 id : 3784 Thread Available is : 8 Thread N° 6 id : 344 Thread Available is : 9 Thread N° 10 id : 3596 Come vediamo Thread Available è variabile in base a diversi fattori, la cosa che salta subito all’occhio e che ci saremmo aspettati dalle print del 2 thread che i Thread disponibili siano minori di 9. Come visto in precedenza il ThreadPool definisce un tempo entro il quale allocare una seconda risorsa per attivare un nuovo Thread. Se la precedente richiesta finisce entro un tempo relativamente breve riutilizza la stessa (e per questo mantiene anche lo stesso ID di processo).
  • 22. Gli eventi Fino ad ora abbiamo creato un Thread passandogli un indirizzo di una Sub Routine dichiarata all’interno della funzione Main(), come detto in precedenza è possibile istanziare una classe, passargli dei parametri e poi eseguire in un thread separato una Routine presente di quella classe. Nell’esempio successivo viene dichiarata una classe che al suo interno dispone di un evento TerminateThread che verrà generato per segnalare al Main() il completamento delle operazioni. Imports System.Threading Module Esempio11 Public WithEvents _Worker As Worker Sub Main() _Worker = New Worker _Worker.TimeSleep = 2000 Dim _ThreadStart As New ThreadStart(AddressOf _Worker.Start) Dim _Thread As New Thread(_ThreadStart) _Thread.Start() Console.WriteLine("Main End") End Sub Private Sub _Worker_TerminateThread(ByVal Result As String) _ Handles _Worker.TerminateThread Console.WriteLine(Result) End Sub End Module Public Class Worker Public Event TerminateThread(ByVal Result As String) Public TimeSleep As Integer Public Sub Start() Thread.Sleep(TimeSleep) RaiseEvent TerminateThread("Worker Completing. TimeSleep is : " & _ TimeSleep) End Sub End Class I risultato dell’elaborazione è il seguente Main End Worker Completing. TimeSleep is : 2000 Questo meccanismo di segnali ci permetterà di avere un controllo preciso su ciò che sta succedendo all’interno del Thread. Ma è possibile anche mettere in attesa un Thread fino a che non si verifichi una condizione per cui far continuare l’elaborazione senza utilizzare i comandi di sincronizzazione Suspend e poi il relativo Resume.
  • 23. Nel codice successivo utilizzeremo il comando AutoResetEvent che ci permette di utilizzare i metodi WaitOne() e Set() con cui mettere in attesa un Thread (WaitOne) e poi successivamente dal Main far continuare l’elaborazione (Set). Oltre a WaitOne() è possibile mettere in attesa il Thread tramite WaitAny() e WaitAll(). WaitAny() mette in attesa il Thread fino a quando un evento venga segnalato. WaitAll() invece mette in attesa il Thread fino a che tutti gli eventi vengano segnalati. Ovviamente l’evento viene segnalato quando viene evocato il metodo Set(). Questo genere di meccanismi ci permettono di controllare perfettamente il codice che viene processato all’interno dei Thread. Imports System.Threading Module Esempio12 Public _AutoResetEvent As AutoResetEvent Sub Main() _AutoResetEvent = New AutoResetEvent(False) Dim _ThreadStart As New ThreadStart(AddressOf Worker) Dim _Thread As New Thread(_ThreadStart) Console.WriteLine("Main : Start Thread") _Thread.Start() Console.WriteLine("Main Slepp 1 Second...") Thread.Sleep(1000) Console.WriteLine("Main Signal Worker Thread") _AutoResetEvent.Set() End Sub Public Sub Worker() Console.WriteLine("Worker thread Active") _AutoResetEvent.WaitOne() Console.WriteLine("Worker thread Completing") End Sub End Module I risultato dell’elaborazione è il seguente Main : Start Thread Worker thread Active Main Slepp 1 Second... Main Signal Worker Thread Worker thread Completing
  • 24. I Timer Il nameSpace System.Threading comprende un oggetto molto utile che andremo ad analizzare che si chiama Timer. Il timer viene in nostro aiuto quando occorre far eseguire porzioni di codice a tempo. Nell’esempio successivo viene mostrato l’utilizzo di un timer, da tenere in considerazione che questo tipo di classe può essere utilizzata anche all’interno dei Windows Service quando non è possibile utilizzare la classe Timer presente nelle Windows Form. Il costruttore della classe Timer prende in input un oggetto di tipo TimerCallBack che identifica la locazione di memoria da andare ad eseguire, lo State, un oggetto contenente i valori da passare al metodo da eseguire, dueTime che specifica il tempo che verrà calcolato prima della prima chiamata (0 vuol dire subito) e infine il Period che corrisponde al tempo in millisecondi tra una chiamata e l’altra. Imports System.Threading Module Esempio13 Sub Main() Dim _TimerCallback As New TimerCallback(AddressOf Worker) Dim _Timer As Timer = New Timer(_TimerCallback, _ Nothing, _ 0, _ 1000) Thread.Sleep(6000) End Sub Public Sub Worker(ByVal State As Object) Console.WriteLine("Worker Now : " & DateTime.Now.ToLongTimeString()) End Sub End Module Il risultato sarà il seguente. Worker Now : 9.27.43 Worker Now : 9.27.44 Worker Now : 9.27.45 Worker Now : 9.27.46 Worker Now : 9.27.47 Worker Now : 9.27.48 Worker Now : 9.27.49
  • 25. Apartment Thread e interoperabilità COM In ultima analisi vediamo in che maniera far collaborare i Thread .NET con il vecchio mondo COM. Per questo motivo avremo senz’altro notato delle proprietà presenti all’interno dell’oggetto Thread denominate SetApartmentState() e GetApartmentState(). L’Apartment State è un contenitore logico presente in un processo dove sono allocati oggetti che condividono gli stessi requisiti di accesso ad un Thread. Tele specifica esiste nella vecchia COM ma non è presente all’interno del Common Language Runtime di .NET. I Componenti COM grazie all’Apartment garantivano una corretta sincronizzazione delle risorse e per questa loro logica di implementazione che occorre modificare i nostri Thread se vogliamo far uso di componenti COM dal loro interno. La proprietà SetApartmentState() prende in input i valori dati dall’enumerazione riportata in seguito per comodità. Public Enum ApartmentState STA = 0 'Single thread Apartment MTA = 1 'MultiThread Apartment unknow = 2 'Non definito End Enum Se non viene dichiarato un ApartmentState() lo stato prelevato dalla proprietà GetApartmentState() sarà impostato su unknow. l’oggetto COM se noterà un Apartment incompatibile utilizzerà un proxy per farlo lavorare correttamente. Per migliorare le performance di comunicazione tra il thread chiamante e l’oggetto COM è meglio specificare il corretto stato di Apartment da utilizzare. Non è possibile modificare una volta avviato il Thread lo stato di Apartment ne annullarne l’inizzializzazione. Imports System.Threading Imports ComDemo Module Esempio14 Sub Main() Dim _ThreadStart As New ThreadStart(AddressOf Worker) Dim _Thread As New Thread(_ThreadStart) Try Console.WriteLine("Worker ApartmentState : " & _ _Thread.GetApartmentState.ToString) _Thread.TrySetApartmentState(ApartmentState.STA) Console.WriteLine("Worker ApartmentState : " & _ _Thread.GetApartmentState.ToString)
  • 26. _Thread.Start() Catch ex As ThreadStateException Console.WriteLine("ThreadStateException occurs " & ex.Message) End Try _Thread.Join() End Sub Sub Worker() Dim ComObject As New ComDemo.Class1 Console.WriteLine("Worker invoke SetValue") ComObject.SetValue("Inizialize") Thread.Sleep(2000) Dim ComValue As String = ComObject.GetValue Console.WriteLine("Worker invoke GetValue : " & ComValue) End Sub End Module Il risultato sarà il seguente. Worker ApartmentState : Unknown Worker ApartmentState : MTA Worker invoke SetValue Worker invoke GetValue : Inizialize Questo esempio fa vedere che è possible impostare l’ApartmentState solamente in fase di creazione del thread prima di aver invocato il metodo Start().
  • 27. Conclusioni Il MultiThreading in Microsoft .NET è possibile ed è di semplice utilizzo come abbiamo appreso in questo documento. Le tecniche di Pool di thread già presenti all’interno del Framework facilitano di gran lunga la realizzazione e la progettazione di applicativi scalabili e performanti che fino ad ora erano onerosi e complessi da realizzare. Al programmatore è rivolta la massima accortezza nello scrivere le applicazioni che con l’avvento del MultiThreading introducono meccanismi complessi di tracciamento dei processi e di Locking.