2011-03-23 87 views
6

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

+2

我刚刚发表了一篇关于如何[使用迭代器在C#4中等待任务]的文章(http://www.codeproject.com/Articles/504197/Await-Tasks-in-Csharp4-using-Iterators)。令人惊讶的是,它既不是“哈克”也不是非常丑陋,AFAICT就像等待。一探究竟! – 2012-12-08 21:33:36

回答

8

await总是涉及同样的转换 - 但这是一个非常痛苦的转变。 一侧的await不是太复杂,但棘手的是,编译器为你建立一个状态机,允许继续跳回到正确的位置。

这是可能我的一些hacky使用迭代块(yield return),你可能会伪造类似的东西......但它会非常难看。

我给出了一个DevExpress webinar on what the compiler is doing behind the scenes a few weeks ago - 它显示了来自两个示例的反编译代码,并解释了编译器如何构建一个返回任务,以及“awaiter”必须做什么。它可能对你有用。

+0

您可以在这里找到Jon在youtube上提到的网络研讨会:http://www.youtube.com/watch?v=HpA2x_JvLD4 – ShitalShah 2014-03-14 20:34:29

2

很少有实现和由迭代器(yield)构成的协程示例。

其中一个例子是Caliburn.Micro框架,它使用此模式进行异步GUI操作。但是对于一般的异步代码,它可以很容易地推广。

2

MindTouch DReAM框架实现协同程序上的迭代器模式的顶部是功能上非常相似,异步/等待:

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

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

Result是梦的版本Task。框架DLL可以与.NET 2.0+一起工作,但是为了构建它,你需要3.5,因为我们现在使用了很多3.5语法。

9

新的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)); 
    } 
} 

由于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将以无处不在的方式使用,但以不同的方式提供它们各自的语义。

0

从我的阅读中,yield returnawait之间的主要区别是,await可以明确地返回一个新的值到延续。

SomeValue someValue = await GetMeSomeValue(); 

而与yield return,你必须通过引用来完成同样的事情。

var asyncOperationHandle = GetMeSomeValueRequest(); 
yield return asyncOperationHandle; 
var someValue = (SomeValue)asyncOperationHandle.Result; 
1

比尔·瓦格纳从微软写道:an article in MSDN Magazine有关如何使用任务并行库在Visual Studio 2010中实现异步样的行为不会对异步CTP添加依赖。

它采用TaskTask<T>广泛里面也有额外的好处,一旦C#5已经出来了,你的代码将做好准备开始使用asyncawait

相关问题