2014-10-10 17 views
6

Pensavo di ottenere un handle su ConfigureAwait, quindi ho provato un esperimento.C# Task ConfigureAwait

La mia comprensione è che ConfigureAwait(false) farà la differenza solo se esiste un contesto di sincronizzazione.

ASP, WPF, ecc. Dovrebbero avere un contesto, ma le app di console e le app di servizio non dovrebbero.

Per vedere come funziona ho fatto un'applicazione Web API e incluso il seguente metodo:

// GET api/values/5 
public async Task<string> Get (int id) 
{ 
    var syncCtx = SynchronizationContext.Current; 

    int startThreadId = Thread.CurrentThread.ManagedThreadId; 

    await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(true); 

    int endThreadId = Thread.CurrentThread.ManagedThreadId; 

    return "Start Thread ID: " + startThreadId.ToString() + 
      ": End Thread ID: " + endThreadId.ToString(); 
} 

La mia previsione era che senza ConfigureAwait o ConfigureAwait set per true, dovrei vedere lo stesso ID filo prima e dopo l'attesa.

I miei primi test hanno mostrato esattamente quello con il vero set come sopra.

Le successive esecuzioni del codice sono iniziate e terminate su ID di thread diversi, indipendentemente da ConfigureAwait.

Ho aggiunto syncCtx per convincermi che ho un contesto.

L'unica avvertenza che ho letto è che se l'attività è stata completata, non ti sarà garantito lo stesso ID. È questo il caso qui? Se è così, perché è così?

Ho impostato un test ingenuo o imperfetto? Se sì, quale sarebbe un test corretto?

Ho iniziato questo percorso in un'app console/servizio e ho realizzato che non stavo ottenendo lo stesso ID thread. Stavo aggiungendo ConfigureAwait(false) come raccomandato nella maggior parte delle annotazioni sulla "best practice" che ho visto. Poiché mi piace vedere come funzionano davvero le cose, ho provato a testare gli ID dei thread. Vedendoli differire mi ha portato attraverso una serie di ricerche che hanno portato al codice di cui sopra.

risposta

8

Fare ConfigureAwait(true) (o semplicemente lasciarlo spento, poiché è il valore predefinito) non lo fa girare sullo stesso thread. Dice per fare un Post o Send con il resto della continueation. In che modo Post o Send esegue effettivamente quel codice non è definito.

WindowsFormsSynchronizationContext e DispatcherSynchronizationContext (contesti di WinForms e WPF) entrambi porranno il seguito nella coda dei messaggi che devono essere elaborati dal Message Pump (il thread dell'interfaccia utente).

D'altra parte, AspNetSynchronizationContext (che è ciò che è in esecuzione sotto) solo stabilisce alcune informazioni di stato verso l'alto (come impostazione HttpContext.Current torna al suo vecchio valore), poi accoda il lavoro sul prossimo filo pool di thread disponibili. Non esiste alcun "Message Pump" per ASP.NET, fa tutto il suo lavoro nel pool di thread, quindi non c'è motivo di cercare di ottenere lo stesso thread dal pool di thread in un secondo momento, quel thread potrebbe già distrutto dal momento in cui avviene la continuazione.

Le volte in cui hai visto lo stesso ID prima e dopo sei stato fortunato e lo stesso thread è stato estratto dal pool di thread prima e dopo la continuazione.

Consiglio vivamente di leggere l'articolo della rivista MSDN "It's All About the SynchronizationContext", viene descritto dettagliatamente come funziona SynchronizationContext e viene fornito un piccolo dettaglio sui 4 tipi di contesti integrati in .NET.

+0

Giusto per chiarire che non sto provando * * per ottenere lo stesso thread, solo cercando di correggere il mio fraintendimento del comportamento di ConfigureAwait. – jeffa00

+0

Va bene, vorrei ancora leggere l'articolo della rivista MSDN. Copre abbastanza bene questi argomenti. –

+0

Ho sicuramente intenzione di leggere quell'articolo. Grazie per il link. – jeffa00

6

SynchronizationContext! = Thread. Se si stesse utilizzando qualcosa come WPF con il suo runtime Dispatcher, allora, si, il suoSynchronizationContext è legato a un thread specifico. Tuttavia cose come ASP.NET e WCF non sono thread affine, ma portano solo con loro un contesto ambientale specifico che deve essere ripristinato su qualunque thread inizieranno ad essere eseguito in seguito. Possono essere affini a un thread specifico pool, ma, ancora una volta, non un thread specifico.

Questo è precisamente il motivo per cui la SynchronizationContext astrazione è stato introdotto: permette diversi quadri di decidere esattamente che cosa significa per loro affinità e permette l'attendono infrastrutture async/a lavorare su di loro in un modo del tutto agnostico.