zoukankan      html  css  js  c++  java
  • .Net中的几种异步模式

    .Net中的几种异步模式

    在C# 5.0引入async之前,存在几种异步编程模式,比如Event-based Asynchronous Pattern(EAP)、IAsyncResult模式(APM)、Task-Based Asynchronous Pattern(TAP)等等。下面以一个简单的简单的例子进行说明:

    1. public class MyClass
    2. {
    3. public int Read(byte[] buffer, int offset, int count);
    4. }

    基于事件的异步模式(EAP)

    1. public class MyClass
    2. {
    3. public void ReadAsync(byte[] buffer, int offset, int count);
    4. public event ReadCompletedEventHandler ReadCompleted;
    5. }
    6. public delegate void ReadCompletedEventHandler(object sender, ReadCompletedEventArgs eventArgs);
    7. public class ReadCompletedEventArgs : AsyncCompletedEventArgs
    8. {
    9. public int Result { get; }
    10. }

    使用这种模式有点复杂,因为你不得不将一个简单的操作分成两个方法,还需要为完成事件派生一个EventArgs类型。上例中如果想要增加其它请求,但是不想要已经注册的完成事件,那么上述的步骤还得再进行一遍。

    IAsyncResult接口

    1. public class MyClass
    2. {
    3. public IAsyncResult BeginRead(
    4. byte[] buffer, int offset, int count, AsyncCallback callback, object state);
    5. public int EndRead(IAsyncResult asyncResult);
    6. }

    这种模式解决了残留的事件处理函数,但是,它依然需要将一个简单操作分成两个方法。
    基于事件的和基于IAsyncResult接口的异步编程模式都需要将一个操作分割成两个方法。而且都通过Object对象来传递参数,并且强制返回一个Object对象,这样会传递一些并不需要的数据。

    简单的异步模式——引入lambda

    如果不使用C# 5.0的async的话,将回调做为一个参数传递给方法可能是最简单的异步模式。

    1. public class MyClass
    2. {
    3. public void ReadAsync(byte[] buffer, int offset, int count, Action<int> callback);
    4. }

    如果使用lambda表达式的话,可以直接将回调当做参数传递,而无需另外定义一个方法。

    1. private void ReadTest(byte[] buffer, int offset,int count)
    2. {
    3. ReadAsync(buffer, offset, count, read =>
    4. {
    5. // Do something
    6. ...
    7. });
    8. }

    这种模式的缺点是代码的可读性会受到影响。如果使用了多个异步API,可能会出现互相嵌套的lambda表达式。

    注意:以上三种模式共同的缺点是任何异常都不会抛出给调用者来处理。而是通过调用异步操作结束方法(EndMethodName)或者获取Result属性来重新抛出异常。

    基于任务的异步模式(TAP)

    .NET 4.0引入了任务并行库(Task Parallel Library),其中最重要的类是Task和它的泛型类Task。使用Task可以实现异步编程:

    1. public class MyClass
    2. {
    3. public Task<int> ReadAsync(byte[] buffer, int offset, int count);
    4. }

    使用ContinueWith方法来注册回调。
    Task的优势是只需要一个方法,这使API变得很简洁。所有复杂的逻辑,包括异常处理和同步上下文都封装在Task类中。


    手动异步编程的问题

    上面的这些异步模式都可以称之为手动异步,它们存在两个共同的问题:

    1. 分割为两个方法。实际方法和回调方法。使用匿名方法或者lambda表达式缓解了这个问题,使代码看起来更加优雅一些,但同时也需要付出代码犬牙交错、难以跟踪的代价。
    2. 如果需要不只一个异步操作,甚至是需要循环调用异步操作,那么就不得不使用一个递归方法,和一个普通的循环相比更难以阅读。
    1. private void LookupHostNames(string[] hostNames)
    2. {
    3. LookUpHostNamesHelper(hostNames, 0);
    4. }
    5. private static void LookUpHostNamesHelper(string[] hostNames, int i)
    6. {
    7. Task<IPAddress[]> ipAddressesPromise = Dns.GetHostAddressesAsync(hostNames[i]);
    8. ipAddressesPromise.ContinueWith(() =>
    9. {
    10. IPAddress[] ipAddresses = ipAddressesPromise.Result;
    11. // Do something with address
    12. ...
    13. if (i + 1 < hostNames.Length)
    14. {
    15. // 递归调用
    16. LookUpHostNamesHelper(hostNames, i + 1);
    17. }
    18. });
    19. }




  • 相关阅读:
    判断大小写数字个数,取交集和并集
    软件工程总结
    正则表达式(邮箱)
    今天距离你生日的天数
    字符数量,查回文
    解决一个表单中的两个或者多个按钮提交到不同的页面中问题
    jsp前台输入框不输入值,后台怎么取出整型?
    第十次作业
    CMD命令行
    Kali渗透安卓手机
  • 原文地址:https://www.cnblogs.com/qianzi067/p/5837993.html
Copyright © 2011-2022 走看看