Używaj instrukcji i słów kluczowych await graj dobrze w języku C #

101

Mam sytuację, w której wykonuję asyncwywołanie metody, która zwraca i IDisposablewystąpienie. Na przykład:

HttpResponseMessage response = await httpClient.GetAsync(new Uri("http://www.google.com"));

Wcześniej asyncbyło na scenie podczas pracy z IDisposableinstancją, to wywołanie i kod używający zmiennej „response” byłyby opakowane w instrukcję using.

Moje pytanie brzmi, czy nadal jest to właściwe podejście, gdy asyncsłowo kluczowe jest wrzucane do miksu? Nawet jeśli kod jest kompilowany, czy instrukcja using nadal będzie działać zgodnie z oczekiwaniami w obu poniższych przykładach?

Przykład 1

using(HttpResponseMessage response = await httpClient.GetAsync(new Uri("http://www.google.com")))
{
    // Do something with the response

    return true;
}

Przykład 2

using(HttpResponseMessage response = await httpClient.GetAsync(new Uri("http://www.google.com")))
{
    await this.responseLogger.LogResponseAsync(response);

    return true;
}
swingdoctor
źródło

Odpowiedzi:

97

Tak, powinno wystarczyć.

W pierwszym przypadku tak naprawdę mówisz:

  • Asynchronicznie poczekaj, aż otrzymamy odpowiedź
  • Użyj go i natychmiast wyrzuć

W drugim przypadku mówisz:

  • Asynchronicznie poczekaj, aż otrzymamy odpowiedź
  • Asynchronicznie poczekaj, aż zarejestrujemy odpowiedź
  • Usuń odpowiedź

usingOświadczenie w sposób asynchroniczny jest „dziwne”, że Disposerozmowa może wykonać w innym wątku do tej, która objęta zasób (w zależności od kontekstu synchronizacji etc), ale będzie jeszcze wydarzy ... zakładając, że coś czekasz oczywiście zawsze się pojawia lub zawiedzie. (Tak jak nie będziesz wywoływał Disposekodu innego niż asynchroniczny, jeśli usinginstrukcja zawiera wywołanie metody, która nigdy nie zwraca).

Jon Skeet
źródło
4
Dzięki Jon. Podczas gdy większość rzeczy asynchronicznych nadal jest dla mnie voodoo, dość pocieszające jest zobaczenie, jak często integruje się z innymi funkcjami .net i po prostu działa
swingdoctor
@JonSkeet Czy powinno czekać nawet na użycie wewnątrz using(){...}bloku, czy jest to przesada lub może w niektórych przypadkach obniżyć wydajność? Czy using(){...}służy temu samemu celowi await?
nam
1
@nam: Nie, usingi awaitsłużą do zupełnie innych celów. Zauważ, że C # 8 ma teraz również asynchroniczne usuwanie. Chociaż to warto być świadomi kwestii gwintowania Moja odpowiedź podkreśla, że na pewno nie znaczy, że to nie tak , aby wymieszać usingi await.
Jon Skeet