posts - 315, comments - 268, trackbacks - 15

My Links

News

View Pietro Libro's profile on LinkedIn

DomusDotNet
   DomusDotNet

Pietro Libro

Tag Cloud

Article Categories

Archives

Post Categories

Blogs amici

Links

PLINQ: Task e Future<T>

La  classe System.Threading.Task.Task è una classe che rappresenta un'operazione asincrona, o meglio, un wrapper di un delegate contenente tutte le informazioni necessarie per conoscere lo stato di avanzamento di un’operazione, ad esempio se è terminata perchè completata o interrotta dall’utente, mediante le proprietà IsCompleted e IsCanceled o per interrompere l'esecuzione dell'operazione, chiamando l’apposito metodo Cancel. Essendo la classe Task priva di costruttori pubblici, per crearne un'istanza è necessario utilizzare uno degli overload del metodo statico Create. Per attendere il termine di un'operazione asincrona, un'istanza della classe Task mette a disposizione il metodo Wait (o meglio tre overload): Wait() senza parametri che attende la conclusione delle operazioni, Wait (TimeSpan span) che accetta un oggetto TimeSpan per indicare il tempo di attesa in millisecondi per il completamento delle operazioni ed infine Wait (int milliseonds) che accetta il numero di millisecondi per cui si vuole attendere la conclusione delle operazioni. Possiamo vedere immediatamente la classe Task in azione con un piccolo esempio. Supponiamo di avere la seguente funzione il cui scopo è creare un file di testo contenente un certo numero di righe, secondo il valore degli argomenti passati:

1 private static void WriteFile(IsolatedStorageFile storage, int start, int count) 2 { 3 string fileName = string.Format("start_{0}.txt", start); 4 5 lock (storage) 6 { 7 using (IsolatedStorageFileStream sw = new IsolatedStorageFileStream(fileName, System.IO.FileMode.OpenOrCreate, storage)) 8 { 9 using (System.IO.StreamWriter swriter = new System.IO.StreamWriter(sw)) 10 { 11 for (int i = start; i < start + count; i++) 12 swriter.WriteLine("Item#{0}", i); 13 } 14 } 15 } 16 }

Supponiamo di voler scrivere 5 file ed attendere per il completamento delle operazioni, possiamo scrivere qualcosa del tipo: 
 

1 Task tFile1 = Task.Create(delegate { WriteFile(storage, 1, 10); }); 2 Task tFile2 = Task.Create(delegate { WriteFile(storage, 11, 10); }); 3 Task tFile3 = Task.Create(delegate { WriteFile(storage, 21, 10); }); 4 Task tFile4 = Task.Create(delegate { WriteFile(storage, 31, 10); }); 5 Task tFile5 = Task.Create(delegate { WriteFile(storage, 41, 10); }); 6 Console.WriteLine("Writing..."); 7 //Attende il completamento di tutti i task, 8 Task.WaitAll(new Task[] { tFile1, tFile2, tFile3, tFile4, tFile5 });


Invece di richiamare il metodo Wait (o uno dei suoi overload per ogni istanza di Task, possiamo richiamare il metodo statico WaitAll (anch'esso definito con tre  overload) che accetta come primo parametro un vettore di oggetti  Task (o da essi derivati), per i quali attendere il completamento delle operazioni. Fin qui tutto bene, ma il metodo WriteFile, esegue l'operazione senza ritornare nessun valore. Supponiamo allora di voler eseguire la scansione "parallela" di un certo numero di directory su disco per ottenere il numero di file di testo (*.txt) contenuti nelle directory. Una funzione molto semplice potrebbe essere di questo tipo:

1 public static int ScanDirectory( string path, string searchPattern) 2 { 3 return System.IO.Directory.GetFiles(path, searchPattern).Count(); 4 }

In questo caso, a meno di creare una classe personalizzata che derivi da Task, possiamo utilizzare la classe Future<T>, che deriva da Task, e che permette il recupero di un valore di ritorno da un'operazione asincrona. Semplicemente possiamo scrivere:

1 Future<int> tSearch1 = Future.Create(() => ScanDirectory(@"C:\", "*.txt")); 2 Future<int> tSearch2 = Future.Create(() => ScanDirectory(@"C:\DIR1", "*.txt")); 3 Future<int> tSearch3 = Future.Create(() => ScanDirectory(@"C:\DIR2", "*.txt")); 4 Task.WaitAll(new Future<int>[] { tSearch1, tSearch2, tSearch3 });

Per quanto detto precedentemente, anche in questo caso è possibile utilizzare il metodo statico WaitAll per attendere il completamento delle operazioni. Per recuperare il numero di file di testo trovati nelle directory, accediamo alla proprietà Value di ogni istanza di Future<int>:

1 int filesCount = tSearch1.Value + tSearch2.Value + tSearch3.Value; 2 Console.WriteLine("Files Count:" + filesCount);

Ci sono delle piccole osservazioni da fare. Supponiamo di modificare la funzione ScanDirectory in questo modo:  

1 public static int ScanDirectory( string path, string searchPattern) 2 { 3 System.Threading.Thread.Sleep(3000); 4 return System.IO.Directory.GetFiles(path, searchPattern).Count(); 5 }

e di eseguire il codice seguente:

1 Future<int> tSearch1 = Future.Create(() => ScanDirectory(@"C:\Lavoro", "*.txt")); 2 tSearch1.Wait(1000); 3 int filesCount = 0; 4 filesCount = tSearch1.Value; 5 Console.WriteLine("Files Count:" + filesCount);

Supponendo che la directory “C:\Lavoro” contenga 4 file di testo, ci si potrebbe aspettare che il valore visualizzato sulla console sia zero (dato che Wait attende per solo un secondo), invece, verrà visualizzato il valore corretto, dato che l’accesso alla proprietà Value  sì comporta come Wait senza parametri. Si potrebbe allora pensare di utilizzare  il metodo Cancel(), subito dopo il Wait, ma verrebbe sollevata un'eccezione  quando si tenta di recuperare  il valore da Value

1 tSearch1.Wait(1000); 2 tSearch1.Cancel();

Una possibile soluzione è quella di sfruttare le proprietà IsCanceled (o in casi diversi IsCompleted), in questo modo:  

1 if(!tSearch1.IsCanceled ) filesCount = tSearch1.Value;

La semplificazione introdotta da queste classi per la gestione delle operazioni asincrone è veramente impressionante.

Print | posted on lunedì 12 gennaio 2009 20:29 | Filed Under [ PLINQ ]

Feedback

Gravatar

# re: PLINQ: Task e Future<T>

Ci mancava tanto ;) E' dalla verisone 1.1 che si trovano in giro classi custom con simili funzionalità.
13/01/2009 12:11 | Tommaso Caldarola
Gravatar

# re: PLINQ: Task e Future<T>

Almeno ora è integrato... :-)
13/01/2009 16:10 | Pietro Libro
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET