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

标签 c# asynchronous task-parallel-library event-based-programming

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

消息是这样通过传输发送的:

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。如果我想像上面的代码块一样从基于事件的异步模式中创建一个任务,它会是什么样子?

最佳答案

关于 TaskCompletionSource,你是对的,它是将 EAP(基于事件的异步模式)转换为 TPL 的 Task 的关键。

此处记录:https://learn.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 和 async 会对此有很大帮助)

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

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
}

关于c# - 在基于事件的异步模式上使用任务并行库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7342170/

相关文章:

ios - 关闭和其他 GCD 问题中的 Weak DispatchGroup

c# - 管理具有不同输出的任务

c# - 任务并行库时间分片

c# - TPL 延续任务 : not sure who is the parent task

c# - 将 int 值转换为渐变中的颜色

C# WinSCP .NET 程序集 : How to upload multiple files asynchronously

c# - 调度准确性,Quartz.NET 与 Rx 2.0

c# - 调用 Async WebAPI 后,控制不会返回到等待的 webClient

c# - 从 Razor View 中的 foreach 局部 View 获取数据

c# - IWebDriver 的 Specflow 上下文注入(inject)失败