2014-08-31 18 views
10

mam trafienia impasu nawet po użyciu ConfigureAwait(false) poniżej przykładowy kod.impasu nawet po użyciu ConfigureAwait (fałsz) w ASP.NET przepływu

Jak na próbki http://blog.stephencleary.com/2012/02/async-and-await.html (#Avoding kontekstu), nie powinno to być hit martwego blokadę.

To moja klasa:

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); 
    } 
} 

Ta klasa jest z udostępnionej biblioteki:

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 
     ... 
} 

Works jeśli dodam ConfigureAwait (fałsz) w oczekiwaniu na połączenie w udostępnionej biblioteki , gdzie wezwanie HttpClient jest wykonany:

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. 
     ... 
} 

Przyjeżdżam na wszystkich blogach znaleziony, jedyną różnicą jest ConfigureAwait znajdę (fałsz) działa w przypadku korzystania z httpClient.AsyncApi() zadzwonić !?

Proszę pomóc wyjaśnić !!!

+0

Twój tytuł mówi * "to nie działa nawet przy użyciu' ConfigureAwait (false) ', ale w kodzie mówisz drugi przykład działa. Który to? –

+0

@Yuval Itzchakov: nie działa, gdy jest używany w 'ProjectsRetriever' i działa, gdy jest używany w' ProjectSystem' –

+1

@ user2746890: 'Byłem pod założeniem, po użyciu ConfigureAwait (false) (gdziekolwiek w stosie wywołań), wykonanie od tego momentu nie spowoduje impasu. "Nie uchwyci kontekstu *, który czeka *. Ale włóż się do inwokacji i oczekuj, a zobaczysz, że 'ProjectSystem.GetProjects' jest wywoływany (i czeka) * przed * wywołujesz' ConfigureAwait (false) 'na zadaniu zwróconym przez' GetProjects'. IMO najlepszą odpowiedzią jest "dostarczanie tylko asynchronicznego API", tj. Tworzenie asynchronizmu 'ProjectsRetriever.GetProjects()'. –

Odpowiedz

14

Z uwag:

byłem pod założenie, po użyciu opcji ConfigureAwait (false) (dowolnego miejsca w stosie wywołań), np xecution z tego punktu nie spowoduje impasu.

Nie wierzę w czarną magię i ty też nie. Zawsze staraj się zrozumieć, co dzieje się, gdy używasz czegoś w swoim kodzie.

Kiedy await metoda, która zwraca asynchroniczny Task lub Task<T> istnieje niejawna zdobyciu SynchronizationContext przez TaskAwaitable generowane metodą Task.GetAwaiter.

Raz, że synchronizacja kontekst jest na swoim miejscu, a wywołanie metody asynchronicznej zakończeniu tych TaskAwaitable prób Marszałek kontynuacji (co jest w zasadzie reszta metoda nazywa po pierwszym await słów kluczowych) na SynchronizationContext (używając SynchronizationContext.Post), który wcześniej złapany. Jeśli wątek wywołujący jest zablokowany, czekając na tę samą metodę, aby zakończyć, masz impel .

Powinieneś zadać sobie pytanie: Should I expose synchronous wrappers for asynchronous methods? 99 procent czasu odpowiedzi: nr. Powinieneś używać synchronicznego API, na przykład jednej oferty WebClient.

+0

Utworzono bibliotekę, którą można łatwo podłączyć do aplikacji ASP.NET, aby wykryć te zakleszczenia i pomóc w ich śledzeniu. Więcej informacji na https://github.com/ramondeklein/deadlockdetection. –

+0

Mogę poczuć twoją pewność siebie, czytając twoją odpowiedź. dobrze odpowiedział. – johni

6

Blokuje gdy używany w ProjectsRetriever ponieważ:

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; 
     ... 
} 

nie blokuje gdy używany w ProjectSystem ponieważ:

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

Przepraszam, to część mnie myli. Czy to dlatego, że HttpClient wykonuje wywołanie spoczynkowe (wykonanie jest przenoszone, nie dzieje się w tym samym kontekście)? –

+0

@ user2746890: która część Cię myli? W pierwszym przykładzie wywołujesz 'await projectClient.GetProjects()' z wątkiem1, więc wątek1 musi kontynuować wykonywanie, ale jest ZABLOKOWANY w 'this.GetProjects (uri) .Result;' –

+0

Byłem w założeniu, raz ConfigureAwait (false) jest używane (dowolne miejsce w stosie wywołań), wykonanie z tego punktu nie spowoduje zakleszczenia. –