Acabo de recibir VS2012 y estoy tratando de manejarlo async
.
Digamos que tengo un método que obtiene algún valor de una fuente de bloqueo. No quiero bloquear la persona que llama del método. Podría escribir el método para tomar una devolución de llamada que se invoca cuando llega el valor, pero como estoy usando C # 5, decido hacer que el método sea asíncrono para que las personas que llaman no tengan que lidiar con devoluciones de llamada:
// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
return Task.Factory.StartNew(() => {
Console.Write(prompt);
return Console.ReadLine();
});
}
Aquí hay un método de ejemplo que lo llama. Si PromptForStringAsync
no fuera asíncrono, este método requeriría anidar una devolución de llamada dentro de una devolución de llamada. Con async, puedo escribir mi método de esta manera muy natural:
public static async Task GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
Console.WriteLine("Welcome {0}.", firstname);
string lastname = await PromptForStringAsync("Enter your last name: ");
Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}
Hasta aquí todo bien. El problema es cuando llamo a GetNameAsync:
public static void DoStuff()
{
GetNameAsync();
MainWorkOfApplicationIDontWantBlocked();
}
El punto GetNameAsync
es que es asíncrono. No quiero que se bloquee, porque quiero volver a MainWorkOfApplicationIDontWantBlocked lo antes posible y dejar que GetNameAsync haga lo suyo en segundo plano. Sin embargo, llamarlo de esta manera me da una advertencia del compilador en la GetNameAsync
línea:
Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
Soy perfectamente consciente de que "la ejecución del método actual continúa antes de que se complete la llamada". Ese es el punto del código asincrónico, ¿verdad?
Prefiero compilar mi código sin advertencias, pero no hay nada que "arreglar" aquí porque el código está haciendo exactamente lo que tengo la intención de hacer. Puedo deshacerme de la advertencia almacenando el valor de retorno de GetNameAsync
:
public static void DoStuff()
{
var result = GetNameAsync(); // supress warning
MainWorkOfApplicationIDontWantBlocked();
}
Pero ahora tengo un código superfluo. Visual Studio parece comprender que me vi obligado a escribir este código innecesario, ya que suprime la advertencia normal de "valor nunca utilizado".
También puedo deshacerme de la advertencia envolviendo GetNameAsync en un método que no es asíncrono:
public static Task GetNameWrapper()
{
return GetNameAsync();
}
Pero ese es un código aún más superfluo. Entonces tengo que escribir código que no necesito o tolerar una advertencia innecesaria.
¿Hay algo sobre mi uso de async que esté mal aquí?
fuente
PromptForStringAsync
, hace más trabajo del que necesita; solo devuelve el resultado deTask.Factory.StartNew
. Ya es una tarea cuyo valor es la cadena ingresada en la consola. No hay necesidad de esperar y devolver el resultado; hacerlo no agrega ningún valor nuevo.GetNameAsync
proporcionar el nombre completo que fue proporcionado por el usuario (es decirTask<Name>
, en lugar de devolver unTask
?DoStuff
Entonces podría almacenar esa tarea, y ya seaawait
que después el otro método, o incluso pasar la tarea a la otra método por lo que podríaawait
oWait
en algún lugar dentro de ella de aplicación.async
palabra clave.Respuestas:
Si realmente no necesita el resultado, simplemente puede cambiar la
GetNameAsync
firma para devolvervoid
:Considere ver la respuesta a una pregunta relacionada: ¿Cuál es la diferencia entre devolver vacío y devolver una Tarea?
Actualizar
Si necesita el resultado, puede cambiar el
GetNameAsync
para devolver, por ejemploTask<string>
:Y úsalo de la siguiente manera:
fuente
GetNameAsync
no devuelven ningún valor (excepto el resultado en sí mismo, por supuesto).void
, no tiene forma de saber cuándo está hecho. A eso me refería cuando dije "el resultado" en mi comentario anterior.async void
método, excepto los controladores de eventos.async void
cualquier excepción que no detecte bloqueará su proceso, pero en .net 4.5 seguirá ejecutándose.Llego bastante tarde a esta discusión, pero también existe la opción de usar la
#pragma
directiva de preprocesador. Tengo un código asíncrono aquí y allá que explícitamente no quiero esperar en algunas condiciones, y no me gustan las advertencias y las variables no utilizadas como el resto de ustedes:El
"4014"
proviene de esta página de MSDN: Advertencia del compilador (nivel 1) CS4014 .Vea también la advertencia / respuesta de @ ryan-horath aquí https://stackoverflow.com/a/12145047/928483 .
Actualización para C # 7.0
C # 7.0 agrega una nueva característica, descartar variables: Descartes - Guía C # , que también puede ayudar a este respecto.
fuente
var
, solo escribe_ = SomeMethodAsync();
No soy particularmente aficionado a las soluciones que asignan la tarea a una variable no utilizada o que cambian la firma del método para devolver void. El primero crea código superfluo y no intuitivo, mientras que el segundo puede no ser posible si está implementando una interfaz o tiene otro uso de la función donde desea usar la Tarea devuelta.
Mi solución es crear un método de extensión de Task, llamado DoNotAwait () que no hace nada. Esto no solo suprimirá todas las advertencias, ReSharper o de otra manera, sino que hará que el código sea más comprensible e indicará a los futuros mantenedores de su código que realmente tenía la intención de que la llamada no fuera esperada.
Método de extensión:
Uso:
Editado para agregar: esto es similar a la solución de Jonathan Allen donde el método de extensión comenzaría la tarea si aún no se hubiera iniciado, pero prefiero tener funciones de un solo propósito para que la intención de la persona que llama sea completamente clara.
fuente
async void
¡ES MALO!Lo que sugiero es que ejecute explícitamente a
Task
través de un método anónimo ...p.ej
O si desea que se bloquee, puede esperar en el método anónimo
Sin embargo, si su
GetNameAsync
método tiene que interactuar con la interfaz de usuario o incluso cualquier cosa vinculada a la interfaz de usuario (WINRT / MVVM, lo estoy mirando), entonces se vuelve un poco más funky =)Deberá pasar la referencia al despachador de la interfaz de usuario de esta manera ...
Y luego, en su método asíncrono, deberá interactuar con su UI o elementos vinculados a la UI que pensaron que el despachador ...
fuente
This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
esto también hace que se cree un nuevo subproceso, mientras que un nuevo subproceso no necesariamente se creará con async / wait solo.Esto es lo que estoy haciendo actualmente:
Donde
RunConcurrently
se define como ...https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs
https://www.nuget.org/packages/Tortuga.Anchor/
fuente
public static void Forget(this Task task) { }
async Task
. Algunas tareas deben iniciarse manualmente.De acuerdo con el artículo de Microsoft sobre esta advertencia, puede resolverla simplemente asignando la tarea devuelta a una variable. A continuación se muestra una traducción del código proporcionado en el ejemplo de Microsoft:
Tenga en cuenta que al hacer esto aparecerá el mensaje "La variable local nunca se usa" en ReSharper.
fuente
Task
-las funciones de retorno deben serawait
-ed a menos que tenga una muy buena razón para no hacerlo. Aquí no hay ninguna razón por la cual descartar la tarea sería mejor que la respuesta ya aceptada de usar unasync void
método.async void
presenta problemas serios en torno al manejo de errores y da como resultado un código no verificable (consulte mi artículo de MSDN ). Sería mucho mejor usar la variable, si está absolutamente seguro de que quiere que se traguen las excepciones en silencio. Lo más probable es que la operación quiera comenzar dosTask
sy luego hacer unawait Task.WhenAll
.async void DoNotWait(Task t) { await t; }
puede usar un método auxiliar simple para evitar los inconvenientes de losasync void
métodos que usted describe. (Y no creo queTask.WhenAll
es lo que quiere el OP, pero podría muy bien ser.)Aquí, una solución simple.
Saludos
fuente
Es su ejemplo simplificado el que causa el código superfluo. Normalmente, desearía utilizar los datos que se obtuvieron de la fuente de bloqueo en algún momento del programa, por lo que desearía recuperar el resultado para poder acceder a los datos.
Si realmente tiene algo que sucede totalmente aislado del resto del programa, async no sería el enfoque correcto. Simplemente comience un nuevo hilo para esa tarea.
fuente
async
fue diseñado para limpiar ( por ejemplo )MethodWithCallback((result1) => { Use(result1); MethodWithCallback((result2) => { Use(result1,result2); })
incluso en este ejemplo trivial, es una perra analizar. Con asíncrono, se genera código equivalente para mí cuando escriboresult1 = await AsyncMethod(); Use(result1); result2 = await AsyncMethod(); Use(result1,result2);
Cuál es mucho más fácil de leer (aunque no estén muy legible aplastado juntos en este comentario!)Use
.¿Realmente quieres ignorar el resultado? como incluir ignorar cualquier excepción inesperada?
Si no es así, es posible que desee echar un vistazo a esta pregunta: enfoque Fire and Forget ,
fuente
Si no desea cambiar la firma del método para devolver
void
(ya que la devoluciónvoid
siempre debe ser anulada ), puede usar la función Descartar C # 7.0+ como esta, que es un poco mejor que asignar a una variable (y debería eliminar la mayoría de los demás advertencias de herramientas de validación de fuente):fuente