Dacurrentsynchronizationcontext, mi manca qualcosa?
-
12-12-2019 - |
Domanda
I Attualmente lavorando Un'applicazione che legge da un file binario grande, che contiene diversi file di migliaia, ogni file viene elaborato da qualche altra classe in applicazione.Questa classe restituisce un oggetto o null.Voglio mostrare un progresso sulla forma principale, ma per qualche motivo non riesco a farne la testa.
int TotalFound = 0;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext;
BufferBlock<file> buffer = new BufferBlock<file>();
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, };
var producer = new ActionBlock<someObject>(largeFile=>{
var file = GetFileFromLargeFile(largeFile);
if(file !=null){
TotalFound++;
buffer.post(file);
lblProgress.Text = String.Format("{0}", TotalFound);
}
}, options);
.
Il codice sopra blocca il mio modulo, anche io uso "Taskscheduler.fromCurrentsynchronizationContext", perché?Perché quando utilizzo il codice sotto i miei aggiornamenti del modulo Fine
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, };
var producer = new ActionBlock<someObject>(largeFile=>{
var file = GetFileFromLargeFile(largeFile);
if(file !=null){
Task.Factory.StartNew(() => {
TotalFound++;
buffer.Post(file);
}).ContinueWith(uiTask => {
lblProgress.Text = String.Format("{0}", TotalFound);
},CancellationToken.None, TaskContinuationOptions.None, uiScheduler);
}
});
.
Sono nuovo a questo intero TPL DataFlow, quindi spera che qualcuno possa condividere un po 'di luce sul perché nel secondo codice Snippet funziona e nel primo snippet no.
Cordiali saluti, Martijn
Soluzione
Il motivo per cui il tuo utente è bloccato è becase stai usando FromCurrentSynchronizationContext
. Fa funzionare il codice sulla filettatura dell'interfaccia utente, il che significa che si congelerà se si effettua una lunga azione da corsa (molto probabilmente GetFileFromLargeFile()
).
D'altra parte, è necessario eseguire il lblProgress.Text
sul filo UI.
Non sono sicuro che dovresti impostare lblProgress.Text
direttamente in questo codice, assomiglia a un accoppiamento troppo stretto a me. Ma se vuoi farlo, penso che dovresti correre solo quella linea sul filo UI:
.
var producer = new ActionBlock<someObject>(async largeFile =>
{
var file = GetFileFromLargeFile(largeFile);
if (file != null)
{
TotalFound++;
await buffer.SendAsync(file);
await Task.Factory.StartNew(
() => lblProgress.Text = String.Format("{0}", TotalFound),
CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
});
Ma una soluzione ancora migliore sarebbe se hai effettuato GetFileFromLargeFile()
asincrono e assicurati che non faccia alcuna azione di lunga durata sulla filettatura dell'interfaccia utente ( ConfigureAwait(false)
può aiutarti con questo). Se lo hai fatto, il codice del ActionBlock
potrebbe funzionare sulla discussione dell'interfaccia utente senza congelare il tuo UI:
.var producer = new ActionBlock<someObject>(async largeFile =>
{
var file = await GetFileFromLargeFile(largeFile);
if (file != null)
{
TotalFound++;
await buffer.SendAsync(file);
lblProgress.Text = String.Format("{0}", TotalFound)
}
}, options);