Así que en los tristes días de C# 4.0, creé la siguiente clase "WorkflowExecutor" que permitía flujos de trabajo asincrónicos en el hilo de la GUI pirateando las continuaciones de "rendimiento de retorno" de IEnumerable para esperar observables. Entonces, el siguiente código, en button1Click, simplemente inicia un flujo de trabajo simple que actualiza el texto, espera que haga clic en el botón 2, y loops después de 1 segundo.esperando en un observable
public sealed partial class Form1 : Form {
readonly Subject<Unit> _button2Subject = new Subject<Unit>();
readonly WorkflowExecutor _workflowExecutor = new WorkflowExecutor();
public Form1() {
InitializeComponent();
}
IEnumerable<IObservable<Unit>> CreateAsyncHandler() {
Text = "Initializing";
var scheduler = new ControlScheduler(this);
while (true) {
yield return scheduler.WaitTimer(1000);
Text = "Waiting for Click";
yield return _button2Subject;
Text = "Click Detected!";
yield return scheduler.WaitTimer(1000);
Text = "Restarting";
}
}
void button1_Click(object sender, EventArgs e) {
_workflowExecutor.Run(CreateAsyncHandler());
}
void button2_Click(object sender, EventArgs e) {
_button2Subject.OnNext(Unit.Default);
}
void button3_Click(object sender, EventArgs e) {
_workflowExecutor.Stop();
}
}
public static class TimerHelper {
public static IObservable<Unit> WaitTimer(this IScheduler scheduler, double ms) {
return Observable.Timer(TimeSpan.FromMilliseconds(ms), scheduler).Select(_ => Unit.Default);
}
}
public sealed class WorkflowExecutor {
IEnumerator<IObservable<Unit>> _observables;
IDisposable _subscription;
public void Run(IEnumerable<IObservable<Unit>> actions) {
_observables = (actions ?? new IObservable<Unit>[0]).GetEnumerator();
Continue();
}
void Continue() {
if (_subscription != null) {
_subscription.Dispose();
}
if (_observables.MoveNext()) {
_subscription = _observables.Current.Subscribe(_ => Continue());
}
}
public void Stop() {
Run(null);
}
}
La parte inteligente de la idea, usando continuaciones "rendimiento" para hacer el trabajo asíncrono, fue tomada desde la idea de Daniel AsyncIOPipe Earwicker: http://smellegantcode.wordpress.com/2008/12/05/asynchronous-sockets-with-yield-return-of-lambdas/, entonces añadí el marco reactiva en la parte superior de la misma.
Ahora estoy teniendo problemas para volver a escribir esto utilizando la función de sincronización en C# 5.0, pero parece que debería ser algo sencillo. Cuando convierto los observables en tareas, solo se ejecutan una vez y el ciclo while se bloquea la segunda vez. Cualquier ayuda para arreglar eso sería genial.
Dicho todo esto/preguntado, ¿qué me proporciona el mecanismo de asincronización/espera que el WorkflowExecutor no lo hace? ¿Hay algo que pueda hacer con async/await que no puedo hacer (dado una cantidad similar de código) con el WorkflowExecutor?
¿Cómo es exactamente lo que hacen que la conversión a 'Task's? ¿Cómo se ve el aspecto? – svick
Y 'await' tiene muchas ventajas sobre este tipo de asincronía, pero una de las grandes diferencias es que regresan de los esperables. P.ej. 'string s = await client.DownloadStringAsync (url);'. – svick