2011-12-08 26 views
6

Cada variación sobre el siguiente código que intento no funciona - si DoSomething() : void y se llama como está escrito, o DoSomething() : Task y se llama con TaskEx.RunEx(), algún intento que implica .GetAwaiter().GetResult(). Los errores vistos incluyen: "Start may not be called on a task with null action", "RunSynchronously may not be called on a task unbound to a delegate" y "The task has not yet completed".Llamar a un método asíncrono de un método no asíncrono

class Program 
{ 
    static void Main(string[] args) // Starting from a non-async method 
    { 
     DoSomething(); 

     Console.WriteLine("Press any key to quit."); 
     Console.ReadKey(); 
    } 

    static async void DoSomething() 
    { 
     Console.WriteLine("Starting DoSomething ..."); 

     var x = await PrepareAwaitable(1); 

     Console.WriteLine("::" + x); 

     var y = await PrepareAwaitable(2); 

     Console.WriteLine("::" + y); 
    } 

    static Task<string> PrepareAwaitable(int id) 
    { 
     return new Task<string>(() => 
     { 
      return "Howdy " + id.ToString(); 
     }); 
    } 
} 

Salida:

partir HacerAlgo ...

Presione cualquier tecla para salir.

PrepareAwaitable s Action 's Task' será más complicado después. Cuando se complete esta acción, sin importar el tiempo que tarde, esperaría que se reanude el Task (u otros mecanismos del Marco) asignando "Howdy ..." a x, y luego a y. Lo que REALMENTE quiero hacer es interceptar los objetos esperados, procesarlos, y en algún momento posterior que controlo, reanudo la continuación con un resultado (x y y). Pero no he ido muy lejos en ese gran paso, así que estoy tratando de comenzar más pequeño.

+1

¿Qué estás * intentando * hacer? Es difícil saber qué significa "no funciona" cuando no sabemos cómo sería el "trabajo". –

+0

Quiero que se ejecute hasta su finalización, por favor. Tal vez me pareció obvio lo que debería estar haciendo debido a cuán equivocado está mi entendimiento. –

+0

@uosef: ¿Pero correr? No hay nada realmente asincrónico aquí? –

Respuesta

5

Las tareas que devolvió aún no se han iniciado (es decir, son tareas "frías"); intente reemplazar el código PrepareAwaitable con lo siguiente:

static Task<string> PrepareAwaitable(int x) 
{ 
    return Task.Factory.StartNew<string>(() => 
    { 
     return "Howdy " + x.ToString(); 
    }); 
} 
+1

Hmm. Eso funciona. ¿Cómo se puede iniciar una tarea en frío más tarde? Cuando intento .Start() en el código original (si DoSomething(): Task) aparece el error "No se puede llamar a Start en una tarea con null action". –

5

Realmente no está claro lo que está tratando de lograr, porque no hay nada asíncrono pasando. Por ejemplo, esto va a compilar y ejecutar, pero no sé si es lo que quiere:

using System; 
using System.Threading.Tasks; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     DoSomething(); 

     Console.WriteLine("Press any key to quit."); 
     Console.ReadKey(); 
    } 

    static async void DoSomething() 
    { 
     Console.WriteLine("Starting DoSomething ..."); 

     var x = await PrepareAwaitable(1); 

     Console.WriteLine("::" + x); 

     var y = await PrepareAwaitable(2); 

     Console.WriteLine("::" + y); 
    } 

    static async Task<string> PrepareAwaitable(int x) 
    { 
     return "Howdy " + x; 
    } 
} 

Tenga en cuenta que esto da una advertencia para PrepareAwaitable porque no hay nada en ella asíncrono; sin expresiones de "espera" Todo el programa se ejecuta de forma síncrona. Otra implementación alternativa de PrepareAwaitable:

static Task<string> PrepareAwaitable(int x) 
{ 
    return TaskEx.Run(() => "Howdy " + x); 
} 

es que más parecido a lo que fueron después?

+1

¡Gracias! TaskEx.Run() es la ruta que estaba esperando. ¿Es esto mejor que 'Task.Factory.StartNew' de CarlosFigueira ()'? "Mejor", lo que significa en línea con el uso previsto de los diseñadores asincrónicos. ¿Qué enfoque tiene menos consecuencias limitantes o son sinónimos? –

+1

@ uosɐs: creo que 'TaskEx.Run' es básicamente una abreviatura. Puede haber algunas diferencias sutiles, pero si las hay, entonces no las conozco de manera directa. –

+0

¡Gracias por la respuesta detallada! Yo dividiría los puntos si pudiera. –

10

Primero, lea el documento Patrón asincrónico basado en tareas. Está bajo My Documents\Microsoft Visual Studio Async CTP\Documentation. Este documento describe cómo diseñar API consumibles de forma natural por await.

En segundo lugar, tenga en cuenta que hay varios aspectos de la clase Task y las API relacionadas que ya no se aplican realmente en el nuevo mundo asincrónico. Task fue escrito originalmente como el núcleo de TPL. Resultó ser una buena opción para la programación asincrónica, por lo que Microsoft lo utilizó en lugar de crear una nueva clase "Promesa". Pero una cantidad de métodos son remanentes, utilizados por TPL pero no necesarios para la programación asincrónica.

Para responder a la pregunta principal, mezclar el código síncrono y asíncrono no es muy sencillo con el CTP asíncrono actual. Escribí a library que incluye un método de extensión Task.WaitAndUnwrapException, que hace que sea fácil llamar al código asincrónico desde el código de sincronización. Puede que también le interese mi AsyncContext class, que hace que el mensaje "presionar cualquier tecla" sea innecesario.

+0

Esta es una gran respuesta. Usted ilustra una comprensión más profunda de mis luchas generales con asincronismo mientras lo estoy aprendiendo, y dividiría los puntos si pudiera. –

Cuestiones relacionadas