satelite estoy donde ahora c# async-await dotnet-httpclient

c# - donde - ¿Debería esperar a ReadAsStringAsync() si espero la respuesta en la que estoy realizando ReadAsStringAsync()?



donde estoy ahora (3)

¿Debería esperar a ReadAsStringAsync() si espero la respuesta en la que estoy realizando ReadAsStringAsync() ? Para aclarar aún más, ¿cuál es la diferencia o el camino correcto entre los siguientes? ¿Son efectivamente iguales?

var response = await httpClient.GetAsync("something"); var content = await response.Content.ReadAsStringAsync(); return new AvailableViewingTimesMapper().Map(content);

O

var response = await httpClient.GetAsync("something"); var content = response.Content.ReadAsStringAsync(); return new AvailableViewingTimesMapper().Map(content.Result);


Aquí está el código fuente de .NET para ReadAsStringAsync . Si profundiza en el método LoadIntoBufferAsync (), verá que esto mantendrá la lectura del búfer de HttpResponse y dará lugar a una posible llamada a la red. Esto significa que es una buena práctica usar aguardar en lugar de Resultado.

[__DynamicallyInvokable] public Task<string> ReadAsStringAsync() { this.CheckDisposed(); TaskCompletionSource<string> tcs = new TaskCompletionSource<string>(); HttpUtilities.ContinueWithStandard(this.LoadIntoBufferAsync(), (Action<Task>) (task => { if (HttpUtilities.HandleFaultsAndCancelation<string>(task, tcs)) return; if (this.bufferedContent.Length == 0L) { tcs.TrySetResult(string.Empty); } else { Encoding encoding1 = (Encoding) null; int index = -1; byte[] buffer = this.bufferedContent.GetBuffer(); int dataLength = (int) this.bufferedContent.Length; if (this.Headers.ContentType != null) { if (this.Headers.ContentType.CharSet != null) { try { encoding1 = Encoding.GetEncoding(this.Headers.ContentType.CharSet); } catch (ArgumentException ex) { tcs.TrySetException((Exception) new InvalidOperationException(SR.net_http_content_invalid_charset, (Exception) ex)); return; } } } if (encoding1 == null) { foreach (Encoding encoding2 in HttpContent.EncodingsWithBom) { byte[] preamble = encoding2.GetPreamble(); if (HttpContent.ByteArrayHasPrefix(buffer, dataLength, preamble)) { encoding1 = encoding2; index = preamble.Length; break; } } } Encoding encoding3 = encoding1 ?? HttpContent.DefaultStringEncoding; if (index == -1) { byte[] preamble = encoding3.GetPreamble(); index = !HttpContent.ByteArrayHasPrefix(buffer, dataLength, preamble) ? 0 : preamble.Length; } try { tcs.TrySetResult(encoding3.GetString(buffer, index, dataLength - index)); } catch (Exception ex) { tcs.TrySetException(ex); } } })); return tcs.Task; }


La razón por la que ReadAsString es un método async es que, en realidad, leer los datos es una Operación IO. Es posible que el contenido no esté completamente cargado, incluso si ya tiene el resultado http. No hay hilos adicionales o grandes cargas de computación involucradas.

HttpClient.GetAsync permite agregar una HttpCompletionOption para que GetAsync solo regrese una vez que se haya cargado el HttpResult completo. En ese caso, HttpContent.ReadAsStringAsync se completará sincrónicamente (un llamado fastpath) porque el contenido ya está allí.

Así que definitivamente deberías esperar.

Además: como probablemente este es un código de biblioteca que no depende de regresar en el subproceso de la interfaz de usuario, debe agregar .ConfigureAwait(false) a todas las llamadas de métodos esperadas.


Tu primer ejemplo es el correcto. El segundo ejemplo no cede durante la operación asíncrona. En cambio, al obtener el valor de la propiedad content.Result , obliga a que el subproceso actual espere hasta que la operación asíncrona haya finalizado.

Además, como señala el comentarista Scott Chamberlain, al bloquear el hilo actual es posible que pueda introducir la posibilidad de un interbloqueo. Eso depende del contexto, pero un escenario común para await es usar esa declaración en el subproceso de la interfaz de usuario, y el subproceso de la interfaz de usuario debe seguir respondiendo a una variedad de necesidades, pero incluso para poder manejar realmente la finalización de una operación esperada .

Si evita el segundo patrón, es decir, recupera el valor de la propiedad Result de una Task que no sabe que ha completado, no solo puede garantizar un uso eficiente de sus hilos, sino que también puede asegurarse contra esta trampa común de interbloqueo.