2011-03-23 88 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

相關問題