2012-05-19 6 views
7

Actualmente estoy tratando de portar una gran cantidad de código síncrono existente a WinRT.¿Cuáles son los riesgos de ajustar Async/Await IAsyncOperations con el código Task.Wait()?

Como parte de esto, estoy teniendo problemas con el código existente esperando que algunas operaciones sean sincrónicas, p. Ej. para el archivo de E/S

Para adaptar el código existente para trabajar con la API de estilo IAsyncOperation dentro WinRT, he utilizado una técnica de envolver la IAsyncOperation con un método de extensión como:

namespace Cirrious.MvvmCross.Plugins.File.WinRT 
{ 
    public static class WinRTExtensionMethods 
    { 
     public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) 
     { 
      var task = operation.AsTask(); 
      task.Wait(); 
      if (task.Exception != null) 
      { 
       // TODO - is this correct? 
       throw task.Exception.InnerException; 
      } 

      return task.Result; 
     } 
    } 
} 

de MvvmCross WinRT ExtensionMethods - con un método similar para IAsyncAction

Estas envolturas parece funcionar - y que me permite utilizar los métodos Async en código sincrónica como:

public IEnumerable<string> GetFilesIn(string folderPath) 
    { 
     var folder = StorageFolder.GetFolderFromPathAsync(ToFullPath(folderPath)).Await(); 
     var files = folder.GetFilesAsync().Await(); 
     return files.Select(x => x.Name); 
    } 

Entiendo que esto no está realmente en el espíritu de WinRT; pero, en primer lugar, espero que, en primer lugar, se invoque a estos métodos en los hilos de fondo; y estoy escribiendo esto con el objetivo de hacer que mi código sea compatible con plataformas cruzadas, incluso para plataformas que todavía no admiten la espera asincrónica y/o para desarrolladores que todavía no están listos para dar el salto.

Entonces ... la pregunta es: ¿qué riesgos corro al usar este tipo de código?

Y como segunda pregunta, ¿hay alguna forma mejor de lograr la reutilización de código para áreas como File I/O?

+0

http://feedproxy.google.com/~r/AyendeRahien/~3/71OP6uo3bTQ/when-using-the-task-parallel-library-wait-is-a-bad- warning-sign –

Respuesta

2

fin voy a responder a esta ....

Y la respuesta es que realmente no puede hacerlo.

Incluso si intenta utilizar algunos de los métodos de limpieza sugeridos en las otras respuestas, entonces finalmente se tocan las excepciones si intenta ejecutar el código en cualquier hilo que haya prometido no bloquearlo, p. Ej. si intenta ejecutar en el subproceso UI o en un subproceso subproceso.

Entonces ... ¡la respuesta es que simplemente tiene que volver a crear ese código heredado para que de alguna manera sea asíncrono!

3

En primer lugar, creo que su método podría ser reescrita como:

public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) 
{ 
    return operation.AsTask().Result; 
} 

Calling Result se forma sincrónica esperar si la tarea no terminó aún. Y lanzará un AgreggateException si falla. Creo que arrojar el InnerException como lo hace es una mala idea, ya que sobrescribe el seguimiento de la pila de la excepción.

En cuanto a su pregunta real, creo que el mayor peligro con el uso de Wait() junto con el código asíncrono son interbloqueos. Si comienza alguna operación que internamente usa await en el hilo de la interfaz de usuario y luego la espera usando Wait() en el mismo hilo, obtendrá un interbloqueo.

Esto no es tan importante si no está Wait() ing en el hilo de UI, pero igual debería evitarlo si es posible, porque va en contra de la idea completa de async.

+0

Gracias por la responder. Con respecto a la excepción, estoy tratando de hacer que el código circundante espere excepciones como FileNotFoundException, y no puedo esperar que manejen AggregatedException. En general, sin embargo, esta es * con suerte * una solución provisional para mí - a más largo plazo, reescribiré las Apis para usar callbacks basados ​​en Action , o tal vez usaré await/async también en las otras plataformas (el soporte es ¡llegando a MonoTouch y MonoDroid cuando vs11/C# 4.5 sale de beta!) – Stuart

+0

FWIW, parece más en línea con la palabra clave await ya que obtiene la excepción interna lanzada en lugar del agregado. –

2

Hay muchas buenas razones para no hacer esto. Véase, por ejemplo http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx

Pero si quieres hacerlo, utilice los getResults() método de la siguiente manera

public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) 
{ 
    try 
    { 
     return operation.GetResults(); 
    } 
    finally 
    { 
     operation.Close(); 
    } 
} 

Envolver la IAsyncOperation en una tarea, como se ha mencionado svick, trabaja también, pero es menos eficiente.

+1

No pude hacer que esto funcione. Probé el siguiente código: var folder = ApplicationData.Current.LocalFolder; var items = folder.CreateItemQuery(). GetItemsAsync(). Await(); y obtuvo: Se produjo una excepción de tipo 'System.InvalidOperationException' en GetResults. Más información: Se ha llamado a un método en un momento inesperado. (Excepción de HRESULT: 0x8000000E). AsTask(). Resultado funcionó. –

+0

Tuve el mismo problema y es por eso que encontré esta publicación. GetResults() no funcionó, pero AsTask(). Result funcionó bien – noggin182

Cuestiones relacionadas