Molto Yield ed un pizzico di multi-Thread

Capita spesso di sentir parlare di qualcosa di nuovo in .NET. Generalmente si tratta di tecniche di programmazione o di oggetti del Framework che non conoscevo, ma raramente escono fuori keyword del linguaggio nuove. Bene, il mio responsabile ha citato "yield". YIELD?! che diavolo è?! rapida ricerca per la traduzione e salta fuori che significa "Precedenza".... merita un'approfondimento!

Iniziamo a capire il funzionamento del comando con un esempio. Abbiamo un programma che deve recuperare ed elaborare un tot di dati, la richiesta a causa di complicatissime query impiega 1 secondo per ogni elemento, l'elaborazione a causa di altrettanto complicati calcoli iper-matriciali impiega 1,5 secondi. Supponiamo di voler elaborare 10 risultati, tempi previsti (1*10)+(1,5*10) = 25 secondi.

L'approccio che vogliamo utilizzare è quello di recuperare tutte le informzazioni, inserirle in una collection e poi ciclarci sopra per ogni elaborazione.

public static IEnumerable GetEnum()

{

int i = 0;

List<int> Enum = new List<int>();

while (i < 10)

{

Thread.Sleep(1000); //Query complicatissima

Enum.Add(i++);

}

return Enum;

}

public static void Print(object x)

{

Thread.Sleep(1500); //Calcoli iper-matriciali

decimal Elapsed = ((decimal)(DateTime.Now.Ticks - Start.Ticks)) / 10000000;

Console.WriteLine(x + ") " + Elapsed.ToString());

}

static void Main(string[] args)

{

Start = DateTime.Now;

IEnumerable Enumeratore = GetEnum(); //Recupero tutte le informazioni

foreach (int x in Enumeratore)

{

Print(x);//Eseguo la computazione

}

Console.ReadLine();

}

Usando questo codice però avrò da aspettare 10 secondi prima che il programma possa stampare a video qualche infoirmazione, e qui entra in campo il comando yeld.

Sostituiamo la funzione precedente con questa

public static IEnumerable GetEnum()

{

int i = 0;

while (i < 10)

{

Thread.Sleep(1000);

yield return i++;

}

yield break;

}

Come possiamo vedere ogni elaborazione impiega sempre 1 secondo.

Come cambia il funzionamento? Ogni volta che viene richiesto foreach (int x in Enumeratore) viene eseguito un ciclo del while fino al primo yield return i++;. il comando yield return restituisce il valore e "congela" l'esecuzione della funzione fino al prossimo foreach. Quando non ci sono più valori yield break; interrompe il flusso della funzione.

Applicando al programma precedente la nuova get enum avremo sempre un'elaborazione di 25 secondi, ma compare a video un'informazione ogni 2.5 sec (1 sec per la query e 1.5 per la computazione), Rispetto a prima in cui rimaneva bloccata l'applicazione per 10 secondi per poi sfornare un risultato ogni 1.5.

Da notare che non è stata fatta alcuna modifica al main, quindi un'eventuale migrazione di codice preesistente non comporta problemi.

Arrivati a queto punto mi è saltato in mente di utilizzare in coppia con yeld il multithred per il notro consumer di dati.

Stavolta ad essere modificato è il main

static void Main(string[] args)

{

Start = DateTime.Now;

IEnumerable Enumeratore = GetEnum(); //Mi aggancio alla funzione per recuperare i valori.

foreach (int x in Enumeratore)

{

Thread Stampa = new Thread(new ParameterizedThreadStart(Print));

Stampa.Start(x);

}

Console.ReadLine();

}

Adesso stiamo chiamando il metodo print in un processo separato (magari è un elaborazione remota).

Come nel metodo precedente il primo risultato lo otterrò dopo 2.5 secondi, ma tutti gli altri ogni 1: totale elaborazione 11.5 secondi contro 25.

Il trucco ovviamente stà nell'elaborazione parallela del multithread.

Ovviamente in questo esempio ero avvantaggiato dal fatto che i tempi sono fissi e che la seconda elaborazione è più lunga della prima, altriemnti avrei dovuto costruirmi dei buffer per gestire i risultati, ma essendo a titolo esemplificativo non era il caso.

Spero che l'articolo vi sia interessato: per domande, chiarimenti e soprattutto correzioni scrivetemi :)

altre informazioni le potete trovare sul sito msdn

Wamba

posted @ domenica 29 aprile 2007 12:42

Print
«aprile»
domlunmarmergiovensab
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011