2014-08-31 6 views
10

Sto premendo il deadlock anche dopo aver utilizzato ConfigureAwait(false), di seguito è riportato il codice di esempio.deadlock anche dopo l'utilizzo di ConfigureAwait (false) nel flusso Asp.Net

Come per il campione http://blog.stephencleary.com/2012/02/async-and-await.html (contesto #Avoding), questo non dovrebbe essere stato bloccato.

Questa è la mia classe:

public class ProjectsRetriever 
{ 
    public string GetProjects() 
    { 
     ... 
     var projects = this.GetProjects(uri).Result; 
     ... 
     ... 
    } 

    private async Task<IEnumerable<Project>> GetProjects(Uri uri) 
    { 
     return await this.projectSystem.GetProjects(uri, Constants.UserName).ConfigureAwait(false); 
    } 
} 

Questa classe è da una libreria condivisa:

public class ProjectSystem 
{ 
    public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName) 
    { 
     var projectClient = this.GetHttpClient<ProjectHttpClient>(uri); 
     var projects = await projectClient.GetProjects(); 
     // code here is never hit 
     ... 
} 

Opere se aggiungo ConfigureAwait (false) in attesa di chiamata nella libreria condivisa , dove viene effettuata la chiamata HttpClient:

public class ProjectSystem 
{ 
    public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName) 
    { 
     var projectClient = this.GetHttpClient<ProjectHttpClient>(uri); 
     var projects = await projectClient.GetProjects().ConfigureAwait(false); 
     // no deadlock, resumes in a new thread. 
     ... 
} 

Ho trovato tutti i blog trovati, solo la differenza che trovo è ConfigureAwait (false) funziona quando viene utilizzata con la chiamata httpClient.AsyncApi() !?

Si prega di aiutare a chiarire !!!

blocchi
+0

Il tuo titolo dice * "non funziona nemmeno quando si utilizza' ConfigureAwait (false) 'ma nel tuo codice dici che il secondo esempio funziona. –

+0

@Yuval Itzchakov: non funziona se usato in 'ProjectsRetriever' e funziona quando usato in' ProjectSystem' –

+1

@ user2746890: 'Ero sotto ipotesi, quando viene usato ConfigureAwait (false) (qualsiasi dove nello stack di chiamate), esecuzione da quel punto non causerà deadlock. Non catturerà il contesto * per quell'attesa *. Ma interrompi le tue invocazioni e ti aspetti e scoprirai che 'ProjectSystem.GetProjects' è invocato (e attende) * prima * che tu chiami' ConfigureAwait (false) 'sull'attività restituita da' GetProjects'. IMO la risposta migliore è "fornire solo un'API asincrona", cioè rendere 'ProjectsRetriever.GetProjects()' async. –

risposta

14

Dai commenti:

ero sotto ipotesi, una volta che viene utilizzato ConfigureAwait (false) (qualsiasi punto dello stack di chiamate), e la xecution da quel punto non causerà deadlock.

Non credo nella magia nera, e nemmeno tu dovresti. Cerca sempre di capire cosa succede quando usi qualcosa nel tuo codice.

Quando si await un metodo asincrono che restituisce un Task o un Task<T>, v'è una cattura implicito della SynchronizationContext dal TaskAwaitable viene generata dal metodo Task.GetAwaiter.

volta che contesto sincronizzazione è a posto e la chiamata metodo asincrono completa, i TaskAwaitable tentativi di marshalling continuazione (che è sostanzialmente il resto del metodo chiama dopo il primo await parola) sul SynchronizationContext (utilizzando SynchronizationContext.Post), che è stata precedentemente catturato. Se il thread chiamante è bloccato, in attesa che lo stesso metodo finisca, si ha un deadlock .

Dovreste chiedervi Should I expose synchronous wrappers for asynchronous methods? 99 per cento del tempo la risposta è no. È necessario utilizzare un'API sincrona, ad esempio le offerte WebClient.

+0

Ho creato una libreria che può essere facilmente inserita nell'applicazione ASP.NET per rilevare questi deadlock e aiutarti a rintracciarli. Maggiori informazioni su https://github.com/ramondeklein/deadlockdetection. –

+0

Potevo sentire la tua sicurezza leggendo la tua risposta. ben risposto. – johni

6

strumento una volta in ProjectsRetriever perché:

public class ProjectsRetriever 
{ 
    public string GetProjects() 
    { 
     //querying the result blocks the thread and wait for result. 
     var projects = this.GetProjects(uri).Result; 
     ... //require Thread1 to continue. 
     ... 
    } 

    private async Task<IEnumerable<Project>> GetProjects(Uri uri) 
    { 
     //any thread can continue the method to return result because we use ConfigureAwait(false) 
     return await this.projectSystem.GetProjects(uri, Constants.UserName).ConfigureAwait(false); 
    } 
} 

public class ProjectSystem 
{ 
    public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName) 
    { 
     var projectClient = this.GetHttpClient<ProjectHttpClient>(uri); 
     var projects = await projectClient.GetProjects(); 
     // code here is never hit because it requires Thread1 to continue its execution 
     // but Thread1 is blocked in var projects = this.GetProjects(uri).Result; 
     ... 
} 

non blocca quando viene utilizzato in ProjectSystem perché:

public class ProjectsRetriever 
{ 
    public string GetProjects() 
    { 
     ... 
     var projects = this.GetProjects(uri).Result; 
     ...//requires Thread1 to continue 
     ... 
    } 

    private async Task<IEnumerable<Project>> GetProjects(Uri uri) 
    { 
     //requires Thread1 to continue 
     return await this.projectSystem.GetProjects(uri, Constants.UserName); 
    } 
} 

public class ProjectSystem 
{ 
    public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName) 
    { 
     var projectClient = this.GetHttpClient<ProjectHttpClient>(uri); 
     var projects = await projectClient.GetProjects().ConfigureAwait(false); 
     // no deadlock, resumes in a new thread. After this function returns, Thread1 could continue to run 
} 
+0

scusa, questa è la parte che mi confonde. È perché HttpClient sta facendo una chiamata di riposo (l'esecuzione viene trasferita, non avviene nello stesso contesto)? –

+0

@ user2746890: quale parte ti confonde? Nel primo esempio, chiamate 'await projectClient.GetProjects()' con Thread1, quindi Thread1 deve continuare l'esecuzione ma è BLOCCATO in 'this.GetProjects (uri) .Result;' –

+0

Ero sotto ipotesi, una volta ConfigureAwait (false) viene utilizzato (in qualsiasi punto dello stack di chiamate), l'esecuzione da quel punto non causerà deadlock. –