Deadlock con async e attendere

Quello che voglio realizzare nel mio programma è il seguente callstack / workflow:

  1. spedizione()
  2. autorizzare()
  3. HttpPost ()

La mia idea era che httpPost() fosse asincrono, mentre gli altri 2 metodi restano non asincroni. Tuttavia, per qualche motivo, non funzionerebbe per me a meno che non avessi fatto 2. + 3. asincrona. Forse ho ancora dei malintesi.

A mio avviso, posso a) usare la parola chiave await quando si chiama il metodo asincrono (questo sospenderà il metodo e continuerà dopo il completamento del metodo asincrono), o b) ommit l’attenda -keyword e invece chiamerà Task.Result del async metodi restituiscono il valore, che bloccherà fino a quando il risultato non sarà disponibile.


Lascia che ti mostri l’esempio funzionante:

  private int dispatch(string options) { int res = authorize(options).Result; return res; } static async private Task authorize(string options) { string values= getValuesFromOptions(options); KeyValuePair response = await httpPost(url, values); return 0; } public static async Task<KeyValuePair> httpPost(string url, List<KeyValuePair> parameters) { var httpClient = new HttpClient(new HttpClientHandler()); HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)); int code = (int)response.StatusCode; response.EnsureSuccessStatusCode(); string responseString = await response.Content.ReadAsStringAsync(); return new KeyValuePair(code, responseString); } 

Lascia che ti mostri l’esempio non lavorativo:

  private int dispatch(string options) { int res = authorize(options).Result; return res; } static private int authorize(string options) { string values= getValuesFromOptions(options); Task<KeyValuePair> response = httpPost(url, values); doSomethingWith(response.Result); // execution will hang here forever return 0; } public static async Task<KeyValuePair> httpPost(string url, List<KeyValuePair> parameters) { var httpClient = new HttpClient(new HttpClientHandler()); HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)); int code = (int)response.StatusCode; response.EnsureSuccessStatusCode(); string responseString = await response.Content.ReadAsStringAsync(); return new KeyValuePair(code, responseString); } 

Ho anche provato ad avere tutti e 3 i metodi non asincroni e sostituire gli httpPost in httpPost con .Result s, ma poi si bloccherà per sempre nella riga HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;

Qualcuno potrebbe illuminarmi e spiegare qual è il mio errore?

Si dispone di un SynchronizationContext e quel contesto viene catturato quando si await modo che la (e) prosecuzione (i) possa essere eseguita in quel contesto.

Stai iniziando un’attività asincrona, pianificando una continenza da eseguire nel tuo contesto principale in un secondo momento.

Quindi, prima che venga eseguita l’operazione asincrona, nel proprio contesto principale è presente un codice che effettua un’attesa di blocco sull’operazione asincrona. La continuazione non può essere pianificata per l’esecuzione perché il contesto è occupato in attesa della continuazione. Stallo classico.

Questo è il motivo per cui è importante “asincrizzare tutto”, come hai fatto nel tuo primo esempio.

Ci sono alcuni hack che possono aggirare il deadlock nel secondo esempio, ma non è ancora qualcosa che dovresti fare. L’intero punto di andare asincrono è di evitare di bloccare il / i thread / i. Se continui a fare un’attesa di blocco sull’attività, comunque stai sconfiggendo lo scopo di andare in asincrono. O rendere tutto asincrono, o nulla asincrono, a meno che tu non abbia una scelta.