Technology Experience

Contenuti gestiti da Igor Damiani
posts - 949, comments - 2741, trackbacks - 15120

My Links

News

  • Questo blog si propone di raccogliere riflessioni, teoriche e pratiche, su tutto quello che riguarda il world-computing che mi sta attorno: programmazione in .NET, software attuale e futuro, notizie provenienti dal web, tecnologia in generale, open-source.

    L'idea è quella di lasciare una sorta di patrimonio personale, una raccolta di idee che un giorno potrebbe farmi sorridere, al pensiero di dov'ero e cosa stavo facendo.

    10/05/2005,
    Milano

Archives

Post Categories

Generale

[70-536, #28] Esecuzione asincrona? Thread!

Ve l'avevo promesso che avrei ricominciato. Dopo questa lunga pausa, dovuta un po' a motivi di lavoro e salute, vediamo oggi di riprendere la mia serie di post per l'esame 70-536.

Questa ripresa dei lavori vede come protagonista la class Thread ed affini, ovvero tutte quelle classi definite nel namespace System.Threading che ci permettono di eseguire operazioni in modo asincrono. Ne avevamo già parlato in passato quando avevamo trattato il nuovo componente BackgroundWorker: con nuovo, intendo con il FX2.0. Il BackgroundWorker internamente fa ovviamente uso di thread secondari, e risolve la maggior parte delle problematiche riguardanti la gestione della UI. Come sappiamo, infatti, non possiamo manipolare i controlli sulla Windows Forms al di fuori del thread che le ha create: se abbiamo un task che dura un po' di tempo, e vogliamo ogni tanto notificare qualcosa su una Label, per esempio, non possiamo farlo in modo diretto come faremmo di solito, ma dobbiamo avere qualche accorgimento in più. Il BackgroundWorker semplifica e gestisce direttamente questi accorgimenti.

Se abbiamo bisogno di qualcosa di più performante, oppure non abbiamo UI da gestire (magari stiamo sviluppando un web-services), possiamo lavorare con la classe Thread. Vediamo come.

Introduzione alla classe Thread
Non mi capita tutti i giorni di sviluppare software che abbiano bisogno di thread secondari. Per questo motivo, ho sviluppato una piccola applicazione di test. In pratica, una semplice Windows Forms che si connette ad un database SQL Server, chiede quanti record si vogliono inserire e poi comincia a lanciare INSERT INTO su una tabella. La tabella non è importante (ha un campo identity ed un varchar(50)): ci basta sapere che per aggiungere 20.000 records, ci metto circa 20 secondi.

All'interno della form, ho creato un metodo privato aggiungiRecord:

private void aggiungiRecord()
{
    
// codice
    
for (int cycle = 0; cycle <= 20000; cycle++)
    {
        msg = "Marca " + cycle.ToString();
        cmd.Parameters[0].Value = msg;
        cmd.ExecuteNonQuery();

        updateUI.BeginInvoke(msg, 
nullnull);
    }
    
// codice
}

Ho eliminato le parti di codice non interessanti. Sul click di un pulsante, vogliamo eseguire il metodo qui sopra in un thread separato, cosicchè questo comincia a fare 20.000 inserimenti senza interrompere il nostro lavoro. Oltre alla classe Thread vera e propria, abbiamo bisogno di una delle due classi: ThreadStart oppure ParameterizedThreadStart. La prima se il metodo che vogliamo eseguire non ha parametri di input (come nel nostro caso), la seconda se invece c'è qualche parametro.
Quindi, per esempio:

ThreadStart start = new ThreadStart(aggiungiRecord);
_th = 
new Thread(start);
_th.Start();

Questo è la tecnica assolutamente più semplice: al click del pulsante, il thread parte ed esegue il codice. Il thread non ha nome, non ha priorità. Se il metodo aggiungiRecord avesse un parametro, dobbiamo usare un altro modalità:

int howMany;
int.TryParse(txtHowManyRecords.Text, out howMany);

ParameterizedThreadStart start = 
new ParameterizedThreadStart(aggiungiRecord);
_th = 
new Thread(start);
_th.Start(howMany);

Ho aggiunto sulla form una TextBox chiamata txtHowManyRecords che l'utente può usare per esprimere quanti record vuole inserire in tabella. Utilizzo il metodo statico TryParse della classe int: il metodo ritorna un bool, e in howMany finisce il numero richiesto. Il thread viene inizializzato con la classe ParameterizedThreadStart: per farlo, ho dovuto ovviamente modificare la firma di aggiungiRecord in questo modo:

private void aggiungiRecord(object o)
{ }

Il thread sta girando...e allora?
Giunti a questo punto, il thread sta girando. Il thread secondario sta eseguendo il codice del metodo aggiungiRecord. Probabilmente, il ciclo for inserito occuperà gran parte del tempo. All'interno di questo ciclo, possiamo notificare sulla UI qualsiasi cosa all'utente, ma con le dovute attenzioni che - credo - esulano un po' dallo scopo di questo post. Basta giocherellare con delegate, con BeginInvoke, InvokeRequired e dintorni, ed il gioco è fatto. Personalmente, avevo studiato il capitolo 14 "Multithreaded User Interfaces" di "Windows Forms Programming in C#", scritto da Chris Sells: credo il miglior capitolo di quel libro.

Maggior controllo sul thread
La classe Thread ha alcune caratteristiche molto interessanti, che consentono al nostro codice di controllarla, magari via UI. Innanzitutto, il thread secondario può girare con una cultura differente da quella standard (proprietà CurrentCulture e CurrentUICulture). Possiamo dargli un nome (proprietà Name) e capire in quale stato si trova (ThreadState, che è un'enum). Possiamo usare il metodo Suspend per interrompere l'esecuzione e il metodo Resume per riprenderla. Il metodo Abort blocca del tutto l'esecuzione del thread.

Vedremo nel prossimo post qualche dettaglio in più, e il download dell'applicazione di esempio che ho creato.

powered by IMHO 1.3

Print | posted on giovedì 27 aprile 2006 21:01 | Filed Under [ Esame 70-536 ]

Feedback

Gravatar

# [70-536, #29] Qualche dettaglio in pi

04/05/2006 13:19 | Technology Experience
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET