PLINQ e non solo
Microsoft ha inaugurato una sezione di MSDN dedicata al parallel computing che contiene documentazione ed estensioni al .NET Framework 3.5 chiamate Parallel Extension, che includono PLINQ (Parallel LINQ). Si tratta in realtà di una preview, non è un prodotto rilasciato ma solo una CTP.
Si tratta di un passaggio fondamentale per semplificare e ampliare l'uso di tecniche di programmazione multithread. Queste tecniche sono fondamentali per sfruttare i nuovi processori multi-core. Ma chi non ne vuole sapere, non dovrebbe nemmeno usare .NET per semplici applicativi "mono-thread": spiegherò il perché raccontando un breve aneddoto.
Questa settimana mi è capitato di ragionare sulle possibili cause di un bug, apparentemente causato da un accesso concorrente a una struttura dati non pensata per avere accessi concorrenti. Attenzione: qualsiasi classe Collection è così, quindi basta modificare una List/List<T>. Poiché il programma non crea thread, né fa uso di tecniche di programmazione asincrona, la cosa sembra inspiegabile. In realtà, tutto il codice di Dispose delle classi che implementano IDisposable è potenzialmente soggetto a chiamate in un thread diverso da quello "principale": è sufficiente che la chiamata a Dispose sia fatta dal Garbage Collector attraverso la Finalize dell'oggetto stesso. Buona norma di una Dispose è di *non* lavorare su altri oggetti (che nel frattempo potrebbero essere stati distrutti), a meno che non siano mantenuti da una variabile statica. In tal caso... mentre la Dispose (chiamata dalla Finalize) modifica una Collection, qualcun altro potrebbe modificare la Collection stessa. Accesso concorrente, integrità della Collection non più garantita.
Su una macchina con un solo core, la probabilità che ciò avvenga è molto bassa (il finalization thread viaggia a una priorità più alta). Ma su macchine multi-core (ne bastano due) la probabilità aumenta perché la diversa priorità non influisce sul thread concorrente (che può usare un altro core).
Morale: pensavamo di esserci liberati dei memory leak... ma ecco qualcosa di nuovo, affascinante e (se non vi piacciono le sfide) un po' terrorizzante. Qualcuno rimpiangerà i tempi del BoundsChecker...
PS: Nella Finalize non si devono usare oggetti esterni e non si devono usare meccanismi di sincronizzazione/lock. Quando la Dispose è chiamata dalla Finalize, tutte queste cose non andrebbero fatte e piuttosto meglio lanciare una bella eccezione... o per lo meno fare un log... sempre che l'oggetto incriminato sia vostro e non di una qualche libreria di terze parti...