2012-03-17 14 views
8

Il seguente codice è una semplificazione di un codice in un'applicazione reale. Il problema di seguito è che verrà eseguito un lungo lavoro nel thread dell'interfaccia utente, anziché un thread in background.Come ottenere un'attività NON da eseguire sul thread dell'interfaccia utente

void Do() 
    { 
     Debug.Assert(this.Dispatcher.CheckAccess() == true); 
     Task.Factory.StartNew(ShortUIWork, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 

    void ShortUIWork() 
    { 
     Debug.Assert(this.Dispatcher.CheckAccess() == true); 
     Task.Factory.StartNew(LongWork, TaskCreationOptions.LongRunning); 
    } 

    void LongWork() 
    { 
     Debug.Assert(this.Dispatcher.CheckAccess() == false); 
     Thread.Sleep(1000); 
    } 

Così Do() viene chiamato normalmente dal contesto dell'interfaccia utente. E così anche ShortUIWork, come definito da TaskScheduler. Tuttavia, LongWork termina anche nel thread dell'interfaccia utente, che, ovviamente, blocca l'interfaccia utente.

Come assicurarsi che un'attività non venga eseguita nel thread dell'interfaccia utente?

+0

'tasks' creare un thread che * non * eseguito su interfaccia utente per definizione. Sei sicuro che 'LongWork' blocca il thread dell'interfaccia utente? – Tigran

+2

@Tigran: TPL utilizza thread di background per impostazione predefinita, non per definizione. –

+1

Il codice che non ripro il problema non aiuta. Un motivo comune per un comportamento come questo è una classe wrapper per un componente COM. COM mantiene gli oggetti delle classi che si pubblicizzano per non supportare alcun tipo di threading sicuro eseguendo automaticamente il marshalling di tutte le chiamate al thread che li ha creati. –

risposta

8

LongRunning è solo un suggerimento per il TaskScheduler. Nel caso dello SynchronizationContextTaskScheduler (come restituito da TaskScheduler.FromCurrentSynchronizationContext()), apparentemente ignora il suggerimento.

Da un lato questo sembra controintuitivo. Dopo tutto, se l'attività è di lunga durata, è improbabile che si desideri eseguirla sul thread dell'interfaccia utente. D'altra parte, secondo MSDN:

LongRunning - Specifica che un compito sarà una lunga-running, funzionamento a grana grossa. Fornisce un suggerimento al TaskScheduler secondo cui è possibile giustificare la sottoscrizione in eccesso di .

Poiché il thread UI non è un thread pool di thread, nessun "oversubscription" (filetto piscina inedia) possono verificarsi, per cui è alquanto senso che il suggerimento non avrà alcun effetto per il SynchronizationContextTaskScheduler.

Indipendentemente da ciò, è possibile aggirare il problema passando di nuovo al task scheduler di default:

void ShortUIWork() 
{ 
    Debug.Assert(this.Dispatcher.CheckAccess() == true); 
    Task.Factory.StartNew(LongWork, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); 
} 
+0

Perché il 'Debug.Assert (this.Dispatcher.CheckAccess() == false);' in 'LongWork' passa, anche se è eseguito sul thread dell'interfaccia utente? –

+0

@Branko: non seguo. LongWork non viene eseguito sul thread dell'interfaccia utente se il codice viene modificato come da mia risposta. Ecco perché passa l'asserzione (ma fallisce con il codice originale). –

+0

Strano, avrei giurato che fosse passato quando inizialmente lo avevo debugato, ma ora fallisce (nel codice originale). O ho colpito qualche strana interazione con il debugger (qualunque cosa sia) o questo è solo un fallimento del cervello da parte mia;) Le mie scuse. –

Problemi correlati