- Learn
- Guida Delphi
- Sviluppo di applicazioni Multithread
Sviluppo di applicazioni Multithread
- di Carlo Marona
I sistemi operativi moderni moderni permettono l’esecuzione contemporanea (quasi!!) di più applicazioni e per questo vengono detti Multitask. Una applicazione può quindi essere eseguita contemporaneamente ad un’altra (per esempio quando navighiamo su internet e contemporaneamente controlliamo la posta, oppure ricerchiamo un file sul disco). Durante la sua esecuzione, una applicazione deve bloccare completamente l’esecuzione quando aspetta che terminino processi lenti come l’accesso ai dischi o la comunicazione con altri computer. Molte volte le applicazioni possono essere organizzate in maniera tale da ovviare a questi colli di bottiglia o in maniera da suddividere la loro esecuzione in più processi, magari ripartiti su più processori in sistemi multiprocessore. Ecco che in alcuni dei moderni sistemi operativi ci vengono in contro i Thread. In questo caso avremo a che fare con sistemi operativi multithread. La differenza tra questi due tipi di sistemi operativi sta nel fatto che nel primo tipo, l’unità elementare di esecuzione è il task (processo), mentre nel secondo è il thread. Nell’ultimo tipo di sistema operativo, possiamo avere dei tasks consistenti in più trhead. La differenza fondamentale tra task e trhead sta nel fatto che i primi non possono condividere lo stesso spazio di indirizzamento mentre invece i secondi, ovviamente se appartenenti alle stesso task (o processo), si. La creazione di un nuovo thread non richiede l’impiego di grandi risorse di sistema. Normalmente in una applicazione si ha un thread definito principale, ed è quello che gestisce i messaggi di Windows e l’esecuzione del codice principale e nessuno, uno o più trhead secondari.
La definizione, la creazione e la gestione di thread non è proprio semplice ed intuitiva. Delphi però, come sempre, ci viene incontro semplificando notevolmente le cose mettendoci a disposizione la classe TThread. L’uso di questa classe è molto semplice. Questa contiene alcune proprietà e metodi tra cui il principale è il metodo Execute. Questo è il metodo il cui codice viene eseguito all’interno del thread. Una volta definita una nuova classe ereditata da TThread, per la creazione della classe è necessario effettuare una chiamata al metodo Create della classe la quale ha il seguente struttura
Create(Suspended : Boolean);
Il parametro Suspended da passare al metodo, permette di definire il comportamento del thread in seguito alla sua creazione. Impostando questo parametro a False (valore predefinito) il thread viene attivato immediatamente dopo essere stato creato. Se il parametro Suspended ha valore true, il thread viene creato e immediatamente sospeso, ovvero la sua esecuzione non ha inizio fino ad una chiamata al metodo Resume della classe TThread. Per sospendere di nuovo il thread dopo il suo avvio, è disponibile il metodo Suspend. Esistono altri metodi che permettono di controllare l’esecuzione del thread come Terminate che decreta l’arresto definitivo del thread.
I metodi descritti precedentemente sono affiancati da alcune proprietà che permettono di acquisire informazioni riguardo allo stato di esecuzione del thread. La proprietà Suspended ci informa sul fatto che il thread è stato sospeso; la proprietà Terminated ci indica che il thread è stato terminato; Priority permette di impostare la priorità di esecuzione del thread su 7 livelli differenti di priorità da un minimo di tpIdle ad un massimo di tpTimeCritical; FreeOnTerminate che permette di specificare se l’oggetto thread creato deve essere distrutto al termine dell’esecuzione del codice del thread.
Vediamo ora come va implementato il codice da eseguire all’interno del thread vero e proprio. Quando si definisce una classe erede di TThread, occorre anche riscrivere il metodo Execute (che nella classe TThread è Virtual e Abstract, ovvero non ha implementazione) facendo l’ovverride del metodo ereditato. Il codice che andremo a scrivere all’interno del metodo Execute, sarà il codice che verrà eseguito all’interno del thread. Ecco come si presenta la struttura del metodo Execute
Type NomeClasse = class(TThread)
private
{ Private declarations }
protected
{ Public declarations }
Procedure Execute; override;
end;
Implementation
Procedure NomeClasse.Execute;
Begin
//Inserire qui il codice da eseguire nel thread
End;
Da notare che il codice del metodo Execute, viene eseguito una volta sola quindi raggiunta l’ultima istruzione del metodo Execute, l’esecuzione del thread si arresta. Per eseguire più volte lo stesso codice all’interno del thread, occorre ricorrere ad un ciclo e, cosa importante, ad ogni esecuzione del ciclo controllare il valore della proprietà Terminated del thread per interrompere il ciclo quando l’esecuzione del thread viene interrotta. Una tipica implementazione del metodo Execute è la seguente
Procedure NomeClasse.Execute;
Begin
While not Terminated do
Begin
//Inserire qui il codice da eseguire nel thread
End;
End;
Una importante considerazione da fare riguardo al metodo Execute, sta nel fatto che non si può eseguire codice che fa riferimento ad oggetti della VCL. Questo perchè gli oggetti della VCL vengono eseguiti in un thread differente da quello in esecuzione all’interno del metodo Execute e quindi si potrebbero avere problemi di accesso alle loro proprietà ed ai lor metodi. Per evitare ciò, la classe TThread mette a disposizione il metodo Synchronize che permette appunto di sincronizzare i due threads in maniera tale da paermettere l’accesso sicuro alle proprietà ed ai metodi dell’oggetto voluto. Esistono anche oggetti della VCL chiamati Thread-Safe che sono stati realizzazti appositamente per l’utilizzo in ambienti multithread e che permettono di gestire gli accessi al loro contenuto in maniera concorrente. Un esempio di questi oggetti è la ThreadList.
Al termine dell’esecuzione del codice thread, è possibile eseguire del codice di pulizia scrivendo un gestore di evento per l’unico evento che mette a disposizione la classe TThread, l’evento OnTerminate.
La classe TThread permette anche di sincronizzare l’esecuzione del suo thread con altri thread. tramite il metodo WaitFor della classe TThread è possibile attendere il termine dell’esecuzione di un’altro thread. Questo metodo non restituisce il controllo finchè l’altro thread non è terminato.
Potrebbe capitare la necessità di intercettare quando un thread ha terminato una certa operazione piuttosto che attendere che l’intero thread completi la sua esecuzione. In questo caso esistono gli oggetti evento (TEvent). Quando un thread deve comunicare ad altri di aver completato la un certa operazione, chiama il metodo SetEvent dell oggetto evento. Per disattivare la segnalazione dell’evento è sufficiente effettuare una chiamata al metodo ResetEvent dell’evento. Gli oggetti evento devono essere creati in un campo d’azione globale per far si che tutti i thread interessati possano accedervi. Il thread che necessita di attendere il completamento di una determinata operazione, crea ed avvia i thread interessati ed effettua una chiamata la metodo WaitFor dell’oggetto evento indicando un intervallo di tempo da attendere per la ricezione del segnale. Il tempo da attendere per il settaggio del segnale è espresso in millisecondi e può essere impostato anche su INFINITE. Al ritorno dalla sua chiamata, il metodo WaitFor restituisce uno dei seguenti valori: wrSignaled, wrTimeout, wrAbandoned, wrError.
L’oggetto evento, o meglio la classe TEvent, non è altro che una struttura wrapper per gli oggetti event di Windows.
Altre strutture permettono di semplificare la gestione di multithread. Per esempio, quando gli oggetti su cui si sta operando non possiedono metodi o proprietà specifiche per operare in ambineti multithread, si può ricorrere alle sezioni critiche. Le sezioni critiche permettono di far accedere ai metodi o alle proprietà di un determinato oggetto un solo thread alla volta, onde evitare conflitti di accesso. Questa struttura è definita nella classe TCriticalSection che possiede due metodi: Acquire, Release. Quando un thread deve accedere alla sezione critica, deve prima chiamare il metodo Acquire per bloccare l’accesso agli altri threads e rilasciare tale blocco tramite il metodo Release al termine dell’operazione.
Se vuoi aggiornamenti su Sviluppo di applicazioni Multithread inserisci la tua email nel box qui sotto:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
La tua iscrizione è andata a buon fine. Se vuoi ricevere informazioni personalizzate compila anche i seguenti campi opzionali:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
I Video di HTML.it
Intel darà Galileo e accessori in prova agli sviluppatori
Morris Beaton è a capo della divisione di Intel dedicata agli sviluppatori e durante una breve chiacchierata lascia intendere che ci […]