11

我正在編寫聯網應用程序。在基於事件的異步模式上使用任務並行庫

消息通過傳輸發送,例如:

Network.SendMessage (new FirstMessage()); 

當此消息類型到達,像這樣我就可以註冊一個事件處理程序被稱爲:

Network.RegisterMessageHandler<FirstMessage> (OnFirstMessageReceived); 

而且會觸發該事件:

public void OnFirstMessageReceived(EventArgs<FirstMessageEventArgs> e) 
{ 
} 

我正在爲我的聯網應用程序編寫自定義身份驗證過程,這需要大約五條消息才能完成。

不使用任務並行庫,我將被迫在前面的事件處理程序的每個過程的下一步代碼,就像這樣:

public void OnFirstMessageReceived(EventArgs<FirstMessageEventArgs> e) 
{ 
    Network.SendMessage(new SecondMessage()); 
} 

public void OnSecondMessageReceived(EventArgs<SecondMessageEventArgs> e) 
{ 
    Network.SendMessage(new ThirdMessage()); 
} 

public void OnThirdMessageReceived(EventArgs<ThirdMessageEventArgs> e) 
{ 
    Network.SendMessage(new FourthMessage()); 
} 

public void OnFourthMessageReceived(EventArgs<FourthMessageEventArgs> e) 
{ 
    // Authentication is complete 
} 

我不喜歡周圍跳躍的想法源代碼來編碼這部分和其中的一部分。這很難理解和編輯。

我聽說任務並行庫大大簡化了這個解決方案。

但是,我使用任務並行庫讀取的許多示例都與啓動活動的任務鏈有關。我的意思是「活躍」什麼,是明確提出,當每個任務可以開始,像這樣:

public void Drink() {} 
public void Eat() {} 
public void Sleep() {} 

Task.Factory.StartNew( () => Drink()) 
      .ContinueWith(() => Eat() ) 
      .ContinueWith(() => Sleep()); 

這是從我的基於事件的異步模式,其中每個事件處理方法僅稱爲相反當收到消息時。

換句話說,我不能做這樣的事情(但我想):

Task.Factory.StartNew( () => OnFirstMessageReceived() ) 
      .ContinueWith(() => OnSecondMessageReceived()) 
      .ContinueWith(() => OnThirdMessageReceived() ) 
      .ContinueWith(() => OnFourthMessageReceived()); 

我讀過this article,但我不太明白。這似乎是我需要與TaskCompletionSource有關。如果我想從基於事件的異步模式(如上面的代碼塊)中完成任務,它會是什麼樣子?

回答

20

對於TaskCompletionSource而言,它是將EAP(基於事件的異步模式)轉換爲TPL任務的關鍵。

這是記錄在這裏:https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/tpl-and-traditional-async-programming#exposing-complex-eap-operations-as-tasks

下面是簡化代碼:

public static class Extensions 
{ 
    public static Task<XDocument> GetRssDownloadTask(
     this WebClient client, Uri rssFeedUri) 
    { 
     // task completion source is an object, which has some state. 
     // it gives out the task, which completes, when state turns "completed" 
     // or else it could be canceled or throw an exception 
     var tcs = new TaskCompletionSource<XDocument>(); 

     // now we subscribe to completed event. depending on event result 
     // we set TaskCompletionSource state completed, canceled, or error 
     client.DownloadStringCompleted += (sender, e) => 
     { 
        if(e.Cancelled) 
        { 
         tcs.SetCanceled(); 
        } 
        else if(null != e.Error) 
        { 
         tcs.SetException(e.Error); 
        } 
        else 
        { 
         tcs.SetResult(XDocument.Parse(e.Result)); 
        } 
     }; 

     // now we start asyncronous operation 
     client.DownloadStringAsync(rssFeedUri); 
     // and return the underlying task immediately 
     return tcs.Task; 
    } 
} 

現在,所有你需要做的,使這些業務鏈,就是設置你的延續(這是不是現在很舒服,和C#5的await和異步將很多幫助吧)

所以,這個代碼可以使用這樣的:

public static void Main() 
{ 
    var client = new WebClient(); 

    client.GetRssDownloadTask(
     new Uri("http://blogs.msdn.com/b/ericlippert/rss.aspx")) 
     .ContinueWith(t => { 
      ShowXmlInMyUI(t.Result); // show first result somewhere 
      // start a new task here if you want a chain sequence 
     }); 

    // or start it here if you want to get some rss feeds simultaneously 

    // if we had await now, we would add 
    // async keyword to Main method defenition and then 

    XDocument feedEric = await client.GetRssDownloadTask(
     new Uri("http://blogs.msdn.com/b/ericlippert/rss.aspx")); 
    XDocument feedJon = await client.GetRssDownloadTask(
     new Uri("http://feeds.feedburner.com/JonSkeetCodingBlog?format=xml")); 
    // it's chaining - one task starts executing after 
    // another, but it is still asynchronous 
} 
+0

這是一個很棒的提示,讓我更容易使用WebClient。謝謝!! –

3

Jeremy Likness的博客文章標題Coroutines for Asynchronous Sequential Workflows using Reactive Extensions (Rx)可能會讓你感興趣。下面是他試圖回答的問題:

這個概念很簡單:我們經常需要一組異步操作來按順序執行。也許你必須從服務中加載一個列表,然後加載選定的項目,然後觸發一個動畫。這可以通過鏈接已完成的事件或嵌套lambda表達式來完成,但有沒有更清晰的方法?

+0

非常有趣。這絕對有用。我會看看還有什麼其他答案,尤其是那些使用任務並行庫的人。 – Jason

相關問題