Pregunta

¿Cómo implementaría algo que funcione de manera similar al CTP asíncrono? await ¿palabra clave?¿Existe una implementación simple que funcione como await en todos los casos, o no await ¿Requiere diferentes implementaciones para diferentes escenarios?

¿Fue útil?

Solución

await siempre implica el mismo tipo de transformación, pero es bastante doloroso.El lado biblioteca de await no es demasiado complicado, pero el truco es que el compilador crea una máquina de estado para usted, lo que permite que la continuación salte al lugar correcto.

Es posible que mi uso hacky de bloques de iterador (rendimiento de retorno) pueda falsificar algo similar ... pero sería bastante feo.

Di un Webinar de DevExpress sobre lo que hace el compilador entre bastidores hace unas semanas -que muestra el código descompilado de un par de ejemplos, además de explicar cómo el compilador construye una tarea para devolver y qué tiene que hacer el "awaiter".Puede resultarle útil.

Otros consejos

El nuevo await La palabra clave tiene una semántica similar a la existente. yield return palabra clave en el sentido de que ambos hacen que el compilador genere la máquina de estado de estilo de continuación por usted.Por lo tanto, es posible piratear algo usando iteradores que tengan algunos de los mismos comportamientos que el CTP asíncrono.

Así es como se vería.

public class Form1 : Form
{
    private void Button1_Click(object sender, EventArgs e)
    {
        AsyncHelper.Invoke<bool>(PerformOperation);
    }

    private IEnumerable<Task> PerformOperation(TaskCompletionSource<bool> tcs)
    {
        Button1.Enabled = false;
        for (int i = 0; i < 10; i++)
        {
            textBox1.Text = "Before await " + Thread.CurrentThread.ManagedThreadId.ToString();
            yield return SomeOperationAsync(); // Await
            textBox1.Text = "After await " + Thread.CurrentThread.ManagedThreadId.ToString();
        }
        Button2.Enabled = true;
        tcs.SetResult(true); // Return true
    }

    private Task SomeOperationAsync()
    {
        // Simulate an asynchronous operation.
        return Task.Factory.StartNew(() => Thread.Sleep(1000));
    }
}

Desde yield return genera un IEnumerable nuestra corrutina debe devolver un IEnumerable.Toda la magia ocurre dentro del AsyncHelper.Invoke método.Esto es lo que pone en marcha nuestra rutina (que se hace pasar por un iterador pirateado).Se necesita especial cuidado para asegurarse de que el iterador siempre se ejecute en el contexto de sincronización actual, si existe, lo cual es importante al intentar simular cómo await funciona en un hilo de interfaz de usuario.Lo hace ejecutando la primera MoveNext sincrónicamente y luego usando SynchronizationContext.Send para hacer el resto desde un subproceso de trabajo que también se utiliza para esperar de forma asincrónica los pasos individuales.

public static class AsyncHelper
{
    public static Task<T> Invoke<T>(Func<TaskCompletionSource<T>, IEnumerable<Task>> method)
    {
        var context = SynchronizationContext.Current;
        var tcs = new TaskCompletionSource<T>();
        var steps = method(tcs);
        var enumerator = steps.GetEnumerator();
        bool more = enumerator.MoveNext();
        Task.Factory.StartNew(
            () =>
            {
                while (more)
                {
                    enumerator.Current.Wait();
                    if (context != null)
                    {
                        context.Send(
                            state =>
                            {
                                more = enumerator.MoveNext();
                            }
                            , null);
                    }
                    else
                    {
                        enumerator.MoveNext();
                    }
                }
            }).ContinueWith(
            (task) =>
            {
                if (!tcs.Task.IsCompleted)
                {
                    tcs.SetResult(default(T));
                }
            });
        return tcs.Task;
    }
}

Todo sobre el TaskCompletionSource fue mi intento de replicar la forma await puede "devolver" un valor.El problema es que la corrutina tiene realmente devolver un IEnumerable ya que no es más que un iterador hackeado.Entonces necesitaba encontrar un mecanismo alternativo para capturar un valor de retorno.

Hay algunas limitaciones evidentes con esto, pero espero que esto les dé una idea general.También demuestra cómo el CLR podría tener un mecanismo generalizado para implementar corrutinas para las cuales await y yield return usaría de manera ubicua, pero de diferentes maneras para proporcionar su respectiva semántica.

Hay pocas implementaciones y ejemplos de corrutinas hechas de iteradores (rendimiento).

Uno de los ejemplos es Caliburn.Micro framework, que usa este patrón para operaciones GUI asíncronas.Pero se puede generalizar fácilmente para el código asincrónico general.

El marco MindTouch DReAM implementa Coroutines sobre el patrón Iterator que es funcionalmente muy similar a Async /Espera:

async Task Foo() {
  await SomeAsyncCall();
}

frente a

IYield Result Foo() {
  yield return SomeAsyncCall();
}

Result es la versión de DReAM de Task.Las DLL de framework funcionan con .NET 2.0+, pero para construirlas necesitas 3.5, ya que estamos usando mucha sintaxis 3.5 estos días.

Bill Wagner de Microsoft escribió un artículo en MSDN Magazine sobre cómopuede usar la biblioteca de tareas paralelas en Visual Studio 2010 para implementar un comportamiento similar al asíncrono sin agregar una dependencia en el ctp asíncrono.

Utiliza Task y Task<T> de forma extensiva, lo que también tiene el beneficio adicional de que una vez que C # 5 esté disponible, su código estará bien preparado para comenzar a usar async y await.

Según mi lectura, las principales diferencias entre yield return y await es que await puede proporcionar explícitamente devolver un nuevo valor en la continuación.

SomeValue someValue = await GetMeSomeValue();

mientras que con yield return, tendrías que lograr lo mismo por referencia.

var asyncOperationHandle = GetMeSomeValueRequest();
yield return asyncOperationHandle;
var someValue = (SomeValue)asyncOperationHandle.Result;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top