c# - 如何在没有异步 CTP 的情况下实现等待

标签 c# .net asynchronous async-await async-ctp

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

最佳答案

新的 await 关键字与现有的 yield return 关键字具有相似的语义,因为它们都会导致编译器为您生成延续式状态机。因此,可以使用与异步 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));
    }
}

因为 yield return 生成一个 IEnumerable 我们的协程必须返回一个 IEnumerable。所有的魔法都发生在 AsyncHelper.Invoke 方法中。这就是让我们的协程(伪装成被黑的迭代器)运行的原因。要特别注意确保迭代器始终在当前同步上下文中执行(如果存在的话),这在尝试模拟 await 如何在 UI 线程上工作时很重要。它通过同步执行第一个 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;
    }
}

关于 TaskCompletionSource 的全部内容是我尝试复制 await 可以“返回”一个值的方式。问题是协程必须实际返回一个IEnumerable,因为它只不过是一个被黑的迭代器。所以我需要想出一种替代机制来捕获返回值。

这有一些明显的局限性,但我希望这能让您大致了解。它还演示了 CLR 如何可以 有一个通用的机制来实现 awaityield return 将普遍使用的协程,但以不同的方式提供它们各自的语义。

关于c# - 如何在没有异步 CTP 的情况下实现等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5401567/

相关文章:

c# - 如何获取这个表达式值model => model.Name?

.net - 在 .NET 中编写虚拟打印机

c# - 什么是 [Serializable] 以及我应该何时使用它?

c# - Mango 快速应用程序切换和大量处理崩溃

javascript - 如果 "sync"-function 在 promise 中运行,它是否也同步?

javascript - 函数式 Javascript BaconJS,如何将更多值推送到事件流?

c# - 使用 Aspose 在 .NET 中将 Excel、PowerPoint、PDF 和 Word 转换为图像

c# - 没有可用的公共(public)构造函数 Unity.Exceptions.InvalidRegistrationException

c# - 如何在事件处理程序内调用 WebBrowser 上的方法?

javascript - 从使用多个异步调用的函数返回