Il calcolo parallelo è sempre più alla portata di linguaggi di programmazione diffusi. Programmi o semplici routine che presentano cicli di elaborazione che possono esistere e coesistere autonomamente sono facilmente gestibili col calcolo parallelo.
Data la mia refrattarietà ad acquisire prodotti di terze parti, primo perché ciò richiede di impararne la logica, che potrebbe essere anche non condivisibile, secondo perché ho un innato desiderio di scoprir l’acqua calda, ho deciso di studiare le basi del Multithreading in C#. In effetti il linguaggio si presta e consente anche soluzioni intelligenti in tal senso. L’articolo parte dal presupposto che il lettore debba avere le basi della programmazione ad oggetti e la conoscenza del meccanismo di delega oltre che dei thread.
Mi trovavo infatti nella spiacevole situazione di dover aumentare le prestazioni di calcolo di un applicazione scritta da terzi in C#. Le routine di calcolo in oggetto erano sequenziali, più cicli annidati ma potenzialmente indipendenti. Ho subito pensato ad una classe che gestisse in modo autonomo i cicli, spezzandoli in n-parti a seconda del numero di processori e che nello stesso tempo consentisse di gestirne lo stato di esecuzione.
La soluzione richiedeva innanzi tutto la definizione di una classe si fatta e successivamente l’adeguamento del codice di calcolo. In questo articolo esporrò la stesura della classe Wrapper, denominata MPBox (Multi-Processor Box), si tratta di un progetto di base per evolvere successivamente in casi più complessi, ma comunque un buon punto di partenza.
Specifiche Iniziali
La classe deve gestire:
- l’elenco dei parametri delle procedure di calcolo (es. myproc(a, b, c …)).
- l’avvio e lo stato di esecuzione delle procedure (es. ciascuna è un thread).
- un evento di terminazione ed una proprietà di stato.
Parametri diversi
Quello di dover gestire parametri diversi in tipo è numero è un bel problema che ho risolto pensando di accentrarli in delle classi ad hoc, derivate da quella esposta di seguito:
public class MyParamsItem { public int min = 0; public int max = 0; public string caption = ""; public MyParamsItem(int Min, int Max, string Caption) { min = Min; max = Max; caption = Caption; } }In pratica se la procedura myproc(a, b, c) ammette i parametri a, b, c, questi verranno integrati in un oggetto derivato MyCustomParamsItem che espone le seguenti proprietà a, b, c, min, max, e caption. La definizione della procedura dovrà essere modificata in myproc(MyCustomParamsItem obj), si tratta del primo step per astrarre le procedure di calcolo.
Parametri diversi -> Classe MyParamsItem
Avvio ed esecuzione delle Procedure
Avvalendosi del sistema di delega del .NET è possibile passare come parametro ad un metodo un riferimento ad una procedura. Fantastico! Ed archiviare da qualche parte questo riferimento. Doppiamente Fantastico!
La classe wrapper denominata MPBox espone un metodo per accodare i riferimenti a procedure e parametri delle stesse in un array:
public void Add(MyProc apProc, MyParamsItem aoParamsItem)
ecco come deve essere fatta la procedura richiamata:
public delegate void MyProc(MyParamsItem aoParamsItem);
una volta accodata procedura e parametri (i parametri definiscono anche il ciclo da applicare, da min a max), si è pronti ad avviare tutti i thread desiderati. Il sistema di threading del .NET gestirà in modo autonomo l’assegnazione di ciascun Thread al processore disponibile.
public void Start()
Avvio Thread -> MPBox.Start()
Terminazione
Fin tanto che I thread sono in esecuzione la proprietà booleana Working è valorizzata a true.
while (mp.Working) { System.Threading.Thread.Sleep(10); }
Al termine di tutti i thread eseguiti, la classe genera un evento:
public event ThreadCustomEvent OnFinish; public delegate void ThreadCustomEvent(object sender, System.EventArgs e);
L’Esempio
L’esempio qui scaricabile consente di indicare un ciclo dal min a max. Questo ciclo allo start, verrà da prima eseguito su un solo processore, successivamente su due, poi tre e così via fino ad n. I risultati a video riportano i tempi impiegati.
Ogni idea o spunto per migliorare la classe ed il suo impego sarà ben accetto.