In attesa delle Task Parallel Library che saranno presenti nella versione 4.0 del Framework e che ora sono in CTP mi sono servito della sintassi che utilizzeremo in futuro per esprimere un ciclo For parallelo.
La firma di uno degli overload del For parallelo delle TPL ad oggi è questo:
public static void For(int fromInclusive,int toExclusive,Action<int> body)
Praticamente possiamo eseguire un’azione (Action<int> body) in parallelo più volte finchè la condizione fromInclusive < toExclusive viene verificata.
Il parametro di tipo intero che viene passato al body è l’indice del counter.
La sintassi con cui possiamo invocare il for parallelo è ad esempio questa:
System.Threading.Parallel.For(0,4,
c =>
{
Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
});
il codice soprastante funziona se avete scaricato la CTP del giugno scorso e avete referenziato l’assembly System.Threading.
Il codice non fa niente di interessante al posto del Console.WriteLine(…) avreste dovuto mettere la parte di codice che volete venga eseguita in parallelo.
La mia versione del For parallelo fa uso della classe ThreadPool presente nel framework e che possiamo utilizzare già oggi, quindi utilizza un differente algoritmo di scheduling e non quello utilizzato dalle nuove TPL (molto più fine e complesso, magari vi spiegherò in qualche post futuro come funziona)…diciamo che hanno scritto qualche riga in più :-) e che risolvono molti più problemi di quelli che la mia classe risolve. Quindi il funzionamento beneficia delle gioie e soffre i dolori che il funzionamento della classe ThreadPool porta con se.
La classe che ho scritto e utilizzato è la seguente:
public class Parallel
{
public static void For(int fromInclusive, int toExclusive, System.Action<int> body)
{
ThreadPool.QueueUserWorkItem(f =>
{
for (int i = fromInclusive; i < toExclusive; i++)
{
ThreadPool.QueueUserWorkItem(p =>
{
Data pl = (Data)p;
pl.Action(pl.Count);
}
, new Data() { Action = body, Count = i });
}
}, null);
}
class Data
{
public Action<int> Action { get; set; }
public int Count { get; set; }
}
}
Il codice di invocazione è il medesimo delle TPL basta evitare di usare la parte System.Threading, ma scrivere solamente:
Parallel.For(0,4,
c =>
{
Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
});
la classe deve essere chiaramente visibile.
Il codice non è stato testato "seriamente" quindi potrebbe essere afflitto da bugs e non tratta problematiche come ad esempio gestione delle eccezioni, sospensione del task, sincronizzazione, partizionamento o altre problematiche legate al parallelismo che sono state gestite all’interno delle TPL…per il mio problema questa classe è più che sufficiente, il giorno che rilasceranno le TPL toglierò la mia classe e avrò bisogno solo di referenziare l’assembly con le nuove classi per sfruttare il lavoro del team di Microsoft.
Se trovate qualche bugs o questa soluzione non risolve il vostro problema o ne crea di nuovi contattatemi qui che magari ne parliamo.
Scarica la solution di esempio