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

Microsoft Parallel Extensions e Download Paralleli

Un piccolo problema risolvibile (volendo) utilizzando  le Microsoft Parallel Extensions per il .Net Framework. Supponiamo di avere un testo HTML contenente un certo numero di collegamenti a file di cui si vuole effettuare il download. Per ottenere una lista dei link, possiamo ad esempio utilizzare la classe Regex ed un'opportuna Regular Expression:

Regex reg = new Regex(regular_expression_pattern); MatchCollection matches = reg.Matches(pageContent);

Ottenuta la collezione matches , possiamo seguire diverse strade per eseguire il download dei file, utilizzando istanze di System.Net.WebClient.

1) Eseguiamo un ciclo for e sequenzialmente eseguiamo il download dei file;

2) Scriviamo del codice per l'esecuzione su più thread utilizzando unThreadPool

3) Utilizziamo le Microsoft Parallel Extensions per il .Net Framework 3.5 (CTP 2008 al momento), scaricabili qui.

Sinceramente qualche settimana fa avrei utilizzato il secondo approccio, ma dato che le Parallel Extensions forniscono classi e metodi che semplificano la parallelizzazione del codice senza entrare nel dettaglio di thread e pool di thread, mi è sembrata una buona occasione per sperimentarle. Dovendo eseguire un ciclo sugli oggetti System.Text.RegularExpressions.Match presenti nella collezione matches, possiamo utilizzare  il metodo Parallel.ForEach, il quale implementa l'esecuzione parallela dello statement foreach. Dovendo condividere alcune  informazioni con i vari thread, come la directory dove depositare il file una volta scaricato e l'indirizzo base del sito (come ad esempio www.contoso.com) necessario alla ricostruzione completa dell'url da cui prelevare i file, invece di seguire strane alchimie, possiamo utilizzare un oggetto ParallelState o meglio ParallelState<DownloadStartInfo>, dove la classe DownloadStartInfo è così definita:

1 private class DownloadStartInfo 2 { 3 private string _baseUrl = ""; 4 private string _directoryTarget = ""; 5 6 public DownloadStartInfo(string baseUrl, string directoryTarget) 7 { 8 _baseUrl = baseUrl; 9 _directoryTarget = directoryTarget; 10 } 11 12 public string BaseUrl 13 { 14 get { return _baseUrl; } 15 set { _baseUrl = value; } 16 } 17 18 public string DirectoryTarget 19 { 20 get { return _directoryTarget; } 21 set { _directoryTarget = value; } 22 } 23 }

L'istanza di oggetto DownloadStartInfo sarà memorizzata all'interno della proprietà ParallelState.ThreadLocalState. Non resta quindi che scrivere il codice della funzione utilizzata per eseguire il Download dei file:

1 private static void DownloadFile(Match m, int i, ParallelState<DownloadStartInfo> ps) 2 { 3 4 try 5 { 6 if (!System.IO.Directory.Exists(ps.ThreadLocalState.DirectoryTarget)) 7 System.IO.Directory.CreateDirectory(ps.ThreadLocalState.DirectoryTarget); 8 9 string relativeUrl = m.Groups["relativeUrl"].Value; 10 string fileName = m.Groups["fileName"].Value; 11 string address = string.Format("{0}{1}/{2}", ps.ThreadLocalState.BaseUrl , relativeUrl, fileName); 12 13 System.Net.WebClient client = new System.Net.WebClient(); 14 15 string fullFilePath = System.IO.Path.Combine(ps.ThreadLocalState.DirectoryTarget, fileName); 16 17 Console.WriteLine("\t\tDownloading {0}", fileName); 18 client.DownloadFile(address, fullFilePath ); 19 Console.WriteLine("\t\tDownload of {0} completed", fileName); 20 21 System.Threading.Thread.Sleep(150); 22 } 23 catch (System.Net.WebException wex) 24 { 25 Console.WriteLine("Error ==> {0}", wex.Message); 26 } 27 catch (Exception ex) 28 { 29 Console.WriteLine("Error ==> {0}", ex.Message); 30 } 31 }

Mettiamo tutto insieme  scrivendo il codice relativo al Parallel.ForEach<>:

1 IEnumerable<Match> ms = matches.Cast<Match>(); 2 3 Parallel.ForEach<Match, DownloadStartInfo>(ms, 4 delegate(){return new DownloadStartInfo("http://www.contoso.com", directoryTarget);}, 5 delegate(Match m, int i, ParallelState<DownloadStartInfo> ps) { DownloadFile(m, i, ps); });

dove il primo Delegate indica la funzione che serve a generare i dati thread-Local (a cui si accede  utilizzando l'oggetto ParallelState<...>) utilizzati localmente dai vari thread, mentre il secondo Delegate specifica il codice  da richiamare ad ogni iterazione del Parallel.ForEach<...,..>. Oltre al metodo Parallel.ForEach, possiamo trovare anche il metodo Parallel.For, questi sono molto simili tra loro, ma entrambi richiedono che l'operazione eseguita da cisascun ciclo sia indipendente dagli altri. Piccola osservazione, per eseguire il codice, è necessarion aggiungere un riferimento all'assembly System.Threading.dll

 

Print | posted on martedì 30 dicembre 2008 18:59 | Filed Under [ PLINQ ]

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET