Stasera ripensavo alla chiaccherata fatta con Raffaele durante
il tragitto "nevoso" verso la pizzeria di San Felice la sera del workshop, a
propostio come sta la testa?, riguardo all'implementazione di ISyncronizeInvoke.
Il problema concreto è (forse era) questo:
Ho una collection che implementa tra le tante cose IBindingList questa
collection è una collection di classi (in definitiva dei WorkerThread) che a
loro volta implementano INotifyPropertyChanged il problema è che una delle
proprietà viene aggiornata da un Thread diverso da quello che ha creato la
classe, se quindi mettiamo in binding la collection con ad esempio un
DataGridView ci becchiamo un sacrosanta exception che ci informa che non
possiamo accedere alla UI da un Thread diverso da quello che ha creato i
controlli, bene doveroso .
Faccio un po' di ricerche e non cavo un ragno dal buco, poi
posto sui NG MS e mi rispondono Andrea Boschin e Raffaele
Rialdi, il primo suggerendomi di dare un'occhiata all'implemetazione del
BackgroundWorker per la versione 1.1 e il secondo postandomi un link al
sito di IDesign
dove trovo l'articolo che ha ispirato colui che ha
realizzato il BackgroundWorker per la 1.1, una prima possibile soluzione è
quindi questa (è uno snippet scritto al volo in IMHO...):
void SafeFire( Delegate del )
{
Delegate[] invocationList = del.getInvocationList();
foreach( Delegate d in invocationList )
{
if( d.Target != null )
{
//Se c'è un handler procediamo
ISynchronizeInvoke isi = d.Target as ISynchronizeInvoke;
if( isi != null && isi.InvokeRequired )
{
isi.Invoke( del,
/* ...args... */ );
}
else
{
del.DynamicInvoke( /* ...args... */ );
}
}
}
}
Fatte un po' di prove il problema persiste perchè alla fine il
Target
nel mio caso è
sempre e solo il CurrencyManager che gestisce i dati quindi non sarà mai ISynchronizeInvoke
Stasera durante il mio girovagare su MSDN trovo
le due classi del titolo del post, faccio un po' di prove e sembrano la
manna dal cielo, un esempio per capirci al volo:
class MyAsyncClass
{
AsyncOperation operation;
SendOrPostCallback safeCallback;
MyForm frm;
public MyAsyncClass( MyForm frm )
{
/*
Creo un "safe" delegate
*/
this.safeCallback = new SendOrPostCallback( SafeCallback );
/*
Mi segno un riferimento alla form
chiamante
questa è una bruttura ma per l'esempio va
bene così
Non sparate sul
"pianista"!
*/
this.frm = frm;
}
public void Start()
{
/*
Mi faccio dare dall'AsyncOperationManager una nuova istanza
di AsyncOperation la classe che sotto sotto gestisce la
sincronizzazione tra i thread
*/
this.operation = AsyncOperationManager.CreateOperation( null );
/*
Creo un thread
e lo faccio partire
*/
Thread t = new Thread( new ThreadStart( WT ) );
t.Start();
}
void WT()
{
for( Int32 i = 1; i <= 10; i++ )
{
/*
Simulo un'operazione lunga
*/
Thread.Sleep( 1000 );
/*
La chiave di tutto, chiedo alla classe AsyncOperation
di postare un nuovo "evento" utilizzando il mio
delegato "safeCallback"
*/
this.operation.Post( this.safeCallback, ( i * 10 ) );
}
}
void SafeCallback( object arg )
{
/*
Questo viene eseguito nel thread
corretto, infatti possiamo accedere
ai controlli senza che il fx se la prenda
a male :-)
*/
Int32 c = ( Int32 )arg;
this.frm.MyLabel.Text = "Hi! " + c.ToString();
}
}
MyAsyncClass altro non è che un grezzissimo BackgroundWorker che
scatena gli eventi in maniera ThreadSafe, indagando ulteriormente si scopre che
il fx stesso fa larghissimo uso di questo sistema (l'accoppiata
AsyncOperationManager / AsyncOperation) ad esempio ovunque ci sia il nuovo
pattern per le operazioni asincrone DoOperationAsync
/ DoOperationCompleted, è il caso ad esempio dellla PictureBox che
espone un nuovo metodo LoadAsync (per caricare un imagine in maniera asincrona)
e il corrispondente evento LoadCompleted che sarà scatenato nel Thread corretto
in cui la PictureBox è stata creata con tutti i vantaggi che questo
comporta.
Adesso non mi resta che proseguire con lo studio del mio scenario
e capire se devo costrurimi un mio ThreadSafeCurrencyManager
(che internamente gestisca gli eventi in questo modo) o se quello che ho
già esposto è più che sufficiente.
Vi terrò aggiornati
.m
powered by IMHO 1.3