2013-08-15 5 views
6

Prima un po 'di informazioni di base. Sono in procinto di rendere il codice della libreria C# esistente adatto per l'esecuzione su WinRT. Poiché una parte minore di questo codice in profondità richiede di eseguire un piccolo file di I/O, per prima cosa abbiamo cercato di mantenere le cose sincrone e utilizzato Task.Wait() per interrompere il thread principale fino a quando non è stato eseguito tutto l'IO.Perché task.Attendere il deadlock su un thread non-ui? O sono solo fortunato?

Abbastanza sicuro, abbiamo rapidamente scoperto che porta a un punto morto.

Mi sono quindi ritrovato a modificare molto codice in un prototipo per renderlo "asincrono". Cioè, stavo inserendo async e attendo parole chiave, e stavo cambiando di conseguenza i tipi di ritorno del metodo. Questo è stato un sacco di lavoro - lavoro troppo insensato in effetti -, ma ho ottenuto il prototipo in questo modo.

poi ho fatto un esperimento, e ho eseguito il codice originale con la dichiarazione Wait su un thread separato:

System.Threading.Tasks.Task.Run(()=> Draw(..., cancellationToken) 

No stallo!

Ora sono seriamente confuso, perché ho pensato di capire come funziona la programmazione asincrona. Il nostro codice non (ancora) usa ConfigureAwait (false) affatto. Quindi tutte le dichiarazioni attese dovrebbero continuare nello stesso contesto in cui sono state invocate. Giusto? I assunto che significa: la stessa discussione. Ora se questo thread ha invocato "Wait", questo dovrebbe anche portare a un deadlock. Ma non è così.

Qualcuno di voi ha una spiegazione chiara e solida?

La risposta a questo determinerà se davvero passerò attraverso il nostro codice inserendo un sacco di parole chiave condizionali asincrone/attese, o se lo manterrò pulito e useremo solo un thread che fa un Wait() qui e lì. Se le continuazioni vengono eseguite da un thread arbitrario non bloccato, le cose dovrebbero andare bene. Tuttavia, se vengono eseguiti dal thread dell'interfaccia utente, potremmo avere dei problemi se la continuazione è dispendiosa dal punto di vista computazionale.

Spero che il problema sia chiaro. In caso contrario, per favore fatemelo sapere.

risposta

7

Ho un async/await intro sul mio blog, in cui spiego esattamente ciò che il contesto è:

È SynchronizationContext.Current, a meno che sia null, nel qual caso è TaskScheduler.Current. Nota: se non c'è corrente TaskScheduler, allora TaskScheduler.Current è lo stesso di TaskScheduler.Default, che è l'utilità di pianificazione del pool di thread.

Nel codice di oggi, di solito si tratta solo se si dispone o meno di un SynchronizationContext; i programmatori di attività non vengono utilizzati molto oggi (ma probabilmente diventeranno più comuni in futuro). Ho un article on SynchronizationContext che descrive come funziona e alcune delle implementazioni fornite da .NET.

WinRT e altri framework UI (WinForms, WPF, Silverlight) forniscono tutti uno SynchronizationContext per il thread principale dell'interfaccia utente. Questo contesto rappresenta solo il thread singolo, quindi se si mescolano il blocco e il codice asincrono, è possibile incontrare rapidamente deadlock. Descrivo il motivo per cui ciò accade in modo più dettagliato in a blog post, ma in sintesi il motivo per cui è deadlock perché il metodo async sta tentando di reinserire il suo SynchronizationContext (in questo caso, riprendere l'esecuzione sul thread dell'interfaccia utente), ma il thread dell'interfaccia utente è bloccato in attesa del completamento del metodo async.

Il pool di thread non dispone di SynchronizationContext (o TaskScheduler, normalmente). Quindi, se si sta eseguendo su un thread pool di thread e si blocca su un codice asincrono, non si bloccherà. Questo perché il contesto catturato è il contesto del pool di thread (che non è legato a un thread particolare), pertanto il metodo async può immettere nuovamente il contesto (eseguendo semplicemente un thread nel thread pool) mentre un altro thread pool thread è bloccato in attesa per il completamento.

La risposta a questo determinerà se io davvero passare attraverso rovinare il nostro codice con l'inserimento di un sacco di async condizionale/attendere parole chiave, o se io tenerlo pulito e basta usare un filo che fa un Wait () qui e li.

Se il codice è async tutto il senso, non dovrebbe guardare disordinato a tutti. Non sono sicuro di cosa intendi per "condizionale"; Vorrei solo fare tutto async. await ha un'implementazione "percorso veloce" che lo rende sincrono se l'operazione è già stata completata.

blocco sul codice asincrono utilizzando un thread in background è possibile, ma ha alcuni avvertimenti:

  1. Non hai contesto interfaccia utente, quindi non è possibile fare un sacco di cose di interfaccia utente.
  2. È ancora necessario "sincronizzare" il thread dell'interfaccia utente e il thread dell'interfaccia utente non deve bloccarsi (ad es., await Task.Run(..), non Task.Run(..).Wait()). Questo è particolarmente vero per le app WinRT.
+0

Grazie per le informazioni. Questo spiega molto. –

+0

Come fare confusione: questo è il codice di libreria esistente che è mirato su più piattaforme (.Net), non solo su WinRT. Vorremmo utilizzare lo stesso codice per tutte le piattaforme, quindi dobbiamo usare #if per evitare attese/asincrone per le piattaforme in cui non è disponibile. Inoltre, non possiamo semplicemente modificare tutte le API esistenti in quelle asincrone, in quanto ciò potrebbe rompere le cose per i clienti esistenti. Tutte queste istruzioni #if rendono il codice un po 'meno leggibile. –

+0

Trovo che il _idea_ di async/attenda piuttosto attraente e ragionevole. Tuttavia, penso che l'effettiva implementazione fornita da Microsoft sia problematica. Il fatto che la semantica di questi costrutti dipenda dal contesto in cui è stato avviato il thread è un errore, credo. Soprattutto perché la semantica del codice del programma dovrebbe dipendere dal contesto di esecuzione il meno possibile (difficile da ragionare). Posso capire che Microsoft vuole aggiungere opzioni, ma penso che l'uso diretto di attesa e sincronizzazione non dovrebbe introdurre tali differenze. –

Problemi correlati