如何在没有Async CTP的情况下实现等待

Ily*_*gan 6 .net c# asynchronous async-await async-ctp

您将如何实现与Async CTP await关键字类似的功能?是否有一个简单的实现,就像await在所有情况下一样,或者是否await需要针对不同场景的不同实现?

Bri*_*eon 9

new await关键字与现有关键字具有相似的语义yield return,因为它们都会使编译器为您生成延续样式状态机.因此,可以使用与Async CTP具有一些相同行为的迭代器来一起破解某些东西.

这是它的样子.

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

因为yield return生成IEnumerable我们的协程必须返回一个IEnumerable.所有的魔法都发生在AsyncHelper.Invoke方法中.这就是我们的协程(伪装成黑客迭代器)的原因.需要特别注意确保迭代器始终在当前同步上下文中执行,如果存在迭代器,则在尝试模拟awaitUI线程的工作方式时非常重要.它通过MoveNext同步执行第一个然后使用SynchronizationContext.Send从工作线程执行其余工作来完成此操作,工作线程也用于异步等待各个步骤.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

关于TaskCompletionSource我的一点点是我尝试复制方式await可以"返回"一个值.问题是协程必须实际返回一个,IEnumerable因为它只不过是一个被黑客攻击的迭代器.所以我需要提出一种替代机制来捕获返回值.

这有一些明显的局限性,但我希望这能给你一般的想法.它还演示了CLR如何可能对于执行该协程一个通用机制await,并yield return会普遍使用,但以不同的方式提供各自的语义.


Jon*_*eet 8

await总是涉及同样的转变 - 但这是一个非常痛苦的转变.该的一侧await是不是太复杂,但棘手的一点是,编译器生成一个状态机的你,让连续跳回到正确的位置.

可能会使用迭代器块(产量返回),你可以伪造类似的东西......但它会非常难看.

几周前我给出了一个关于编译器在幕后做什么的DevExpress网络研讨会 - 它显示了几个例子的反编译代码,以及解释编译器如何构建一个返回的任务,以及"awaiter"有什么做.它可能对您有用.