在基于事件的异步模式上使用任务并行库

我正在写一个网络应用程序。

邮件通过传输方式发送:

Network.SendMessage (new FirstMessage() ); 

我可以注册一个事件处理程序,当这个消息类型到达时调用,如下所示:

 Network.RegisterMessageHandler (OnFirstMessageReceived); 

事件被解雇了:

 public void OnFirstMessageReceived(EventArgs e) { } 

我正在为我的网络应用程序编写自定义身份validation过程,这需要大约五条消息才能完成。

不使用任务并行库,我将被迫编写前面的事件处理程序中的每个过程的下一步,如下所示:

 public void OnFirstMessageReceived(EventArgs e) { Network.SendMessage( new SecondMessage() ); } public void OnSecondMessageReceived(EventArgs e) { Network.SendMessage( new ThirdMessage() ); } public void OnThirdMessageReceived(EventArgs e) { Network.SendMessage( new FourthMessage() ); } public void OnFourthMessageReceived(EventArgs 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() ); 

我读过这篇文章 ,但我不太明白。 看起来我需要与TaskCompletionSource 。 如果我想从基于事件的异步模式(如上面的代码块)中创建任务,它会是什么样子?

你对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 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(); // 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等待和异步将帮助很多)

所以,这段代码可以像这样使用:

 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 } 

Jeremy Likness有一个博客文章标题Coroutines for Asynchronous Sequential Workflows using Reactive Extensions(Rx)您可能感兴趣。 以下是他试图回答的问题:

这个概念很简单:我们经常需要一组异步操作来按顺序执行。 也许您必须从​​服务加载列表,然后加载所选项目,然后触发动画。 这可以通过链接已完成的事件或嵌套lambda表达式来完成,但是有更简洁的方法吗?