好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

.NET(C#) TPL:TaskFactory.FromAsync与委托的异步调用

.NET(C#) TPL:TaskFactory.FromAsync与委托的异步调用

目录

1. 简单的委托异步调用 2. 带有异常的委托异步调用 3. 有ref或out的委托异步调用 4. .NET Framework中预定义的异步调用

返回目录 1. 简单的委托异步调用

看一个非常简单的C#委托异步调用:

static   void  Main()

{

     //定义委托

     var  del  =   new   Func < string ,  char ,  int > (doo);

     //调用BeginInvoke

    del . BeginInvoke( "a" ,  'b' , callback, del);

   

     Console . ReadKey();

}

static   void  callback( IAsyncResult  ar)

{

     //从AsyncState中提取委托

     var  del  =  ( Func < string ,  char ,  int > )ar . AsyncState;

     Console . WriteLine( "callback调用" );

     //调用EndInvoke

     var  res  =  del . EndInvoke(ar);

     Console . WriteLine( "doo返回值:{0}" , res);

}

//异步执行方法

static   int  doo( string  a,  char  b)

{

     Console . WriteLine( "doo调用:{0} {1}" , a, b);

     return   1 ;

}

上述代码会输出:

doo调用:a b

doo返回值:1

doo被异步调用,同时EndInvoke方法被调用后,其返回值被输出。

上面的代码存在异步调用中常见的一些繁琐问题:

必须把委托作为参数传入到回调方法中(当然使用Lambda或者匿名委托捕获外部变量可以避免) 必须在回调方法中调用委托的EndInvoke,并且传入IAsyncResult参数从而使异步调用完成。

.NET 4.0 TPL中的TaskFactory.FromAsync可以很大程度上简化异步操作,于是上面的代码简化成这样:

static   void  Main()

{

     var  del  =   new   Func < string ,  char ,  int > (doo);

     Task < int >. Factory . FromAsync(del . BeginInvoke, del . EndInvoke,  "a" ,  'b' ,  null )

         . ContinueWith(t  =>   Console . WriteLine( "doo返回值:{0}" , t . Result));

     Console . ReadKey();

}

//异步执行方法

static   int  doo( string  a,  char  b)

{

     Console . WriteLine( "doo调用:{0} {1}" , a, b);

     return   1 ;

}

注意FromAsync是泛型方法,会根据BeginInvoke委托来匹配后面的参数类型(事实上这是Visual Studio的功劳),那么实际上面的FromAsync完整泛型调用是这样的:

Task < int >. Factory . FromAsync < string ,  char > (del . BeginInvoke, del . EndInvoke,  "a" ,  'b' ,  null )

     . ContinueWith(t  =>   Console . WriteLine( "doo返回值:{0}" , t . Result));

注意此时不需要传入异步调用的而外参数,所以FromAsync的state参数总是为null。

返回目录 2. 带有异常的委托异步调用

对于带有异常的委托异步调用,同样,我们必须调用委托对象的EndInvoke来捕获异常,如下代码:

static   void  Main()

{

     var  del  =   new   Action (doo);

    del . BeginInvoke(callback, del);

     Console . ReadKey();

}

//回调方法

static   void  callback( IAsyncResult  ar)

{

     Console . WriteLine( "callback调用" );

     var  del  =  ( Action )ar . AsyncState;

     try

    {

        del . EndInvoke(ar);

    }

     catch  ( Exception  ex)

    {

         Console . WriteLine( "捕获doo异常:{0}" , ex . Message);

    }

}

//异步执行方法

static   void  doo()

{

     Console . WriteLine( "doo调用" );

     throw   new   Exception ( "doo错误" );

}

代码会输出:

doo调用

callback调用

捕获doo异常:doo错误

现在我们一TPL的方式简化操作:

static   void  Main()

{

     var  del  =   new   Action (doo);

     Task . Factory . FromAsync(del . BeginInvoke, del . EndInvoke,  null );

     Console . ReadKey();

}

//异步执行方法

static   void  doo()

{

     Console . WriteLine( "doo调用" );

     throw   new   Exception ( "doo错误" );

}

输出很有意思,竟然是:

doo调用

没有异常抛出,但是doo方法又确实运行了!原因则是Task运行对异常处理的特殊机制,读者可以参考我的另一篇文章:

.NET(C#) TPL:Task中未觉察异常和TaskScheduler.UnobservedTaskException事件

这个异常可以通过强行垃圾回收,此时这个未觉察状态的异常会在垃圾回收时终结器执行线程中被抛出。如下代码(修改Main方法如下,其他代码不变):

var  del  =   new   Action (doo);

Task . Factory . FromAsync(del . BeginInvoke, del . EndInvoke,  null );

//确保任务完成

Thread . Sleep( 1000 );

//强制垃圾会受到

GC . Collect();

//等待终结器处理

GC . WaitForPendingFinalizers();

输出:

doo调用

Unhandled Exception: System.AggregateException: A Task's exception(s) were not o

bserved either by Waiting on the Task or accessing its Exception property. As a

result, the unobserved exception was rethrown by the finalizer thread. ---> Syst

em.Exception: doo错误

Server stack trace:

   at Mgen.Program.doo() in E:\Users\Mgen\Documents\Visual Studio 2010\Projects\

ConsoleApplication1\ConsoleApplication1\Program.cs:line 34

...(省略)

这个异常会被抛出。

当然根据本例,如果想捕获异常,可以使用Task.Wait来等待Task结束并通过AggregateException.InnerExceptions接收异常:

var  del  =   new   Action (doo);

var  task  =   Task . Factory . FromAsync(del . BeginInvoke, del . EndInvoke,  null );

try

{

    task . Wait();

}

catch  ( AggregateException  ae)

{

     foreach  ( var  ex  in  ae . InnerExceptions)

         Console . WriteLine( "doo方法异常:{0}" , ex . Message);

}

不过上面的代码我的方法调用就不是异步的了,因此更好的方法还是使用ContinueWith,等Task结束后,通过Task.Exception(同样返回AggregateException对象)来枚举异常,如下代码:

var  del  =   new   Action (doo);

Task . Factory . FromAsync(del . BeginInvoke, del . EndInvoke,  null )

     . ContinueWith(t  =>

        {

             foreach ( var  ex  in  t . Exception . InnerExceptions)

                 Console . WriteLine( "doo方法异常:{0}" , ex . Message);

        });

Console . ReadKey();

输出正确:

doo调用

doo方法异常:doo错误

上面都是讲怎样捕获异常,如果你想忽略这个异常(同样不想让这个异常在垃圾回收时在终结器执行线程中被抛出)可以简单的引用一下Exception属性,TaskContinuationOptions.OnlyOnFaulted来使引用Exception属性只发生在发生异常时(即Exception为null的时候没必要再去引用它)。就像这样:

var  del  =   new   Action (doo);

Task . Factory . FromAsync(del . BeginInvoke, del . EndInvoke,  null )

     . ContinueWith(t  =>  {  var  exp  =  t . Exception; },  TaskContinuationOptions . OnlyOnFaulted);

Console . ReadKey();

返回目录 3. 有ref或out的委托异步调用

对于此类委托异步调用,由于委托泛型定义不支持ref和out参数,所以此类委托异步调用无法用FromAsync完成,只能用原始委托异步调用来做。

代码:

delegate   void   MyDelegate ( string  a,  out   int  b);

static   void  Main()

{

     var  del  =   new   MyDelegate (doo);

     int  b;

     var  ar  =  del . BeginInvoke( "a" ,  out  b, callback, del);

     Console . ReadKey();

}

static   void  callback( IAsyncResult  ar)

{

     var  del  =  ( MyDelegate )ar . AsyncState;

     Console . WriteLine( "callback调用" );

     int  res;

    del . EndInvoke( out  res, ar);

     Console . WriteLine( "doo out参数值:{0}" , res);

}

输出:

doo调用

callback调用

doo out参数值:71

返回目录 4. .NET Framework中预定义的异步调用

.NET Framework中也定义了许多类似委托异步调用的方法,其实此类异步调用又称 APM:异步编程模式 (全称Asynchronous Programming Model),此类异步方法的命名以Beginxxx和Endxxx。TaskFactory.FromAsync处理.NET Framework中预定义的异步调用同样游刃有余。

原因是TaskFactory.FromAsync定义了诸多重载可以满足多数预定义的异步调用,比如下面是一个比较长的重载:

//TaskFactory.FromAsync的重载之一

public   Task < TResult >  FromAsync < TArg1, TArg2, TArg3 > ( Func < TArg1, TArg2, TArg3,  AsyncCallback ,  object ,  IAsyncResult >  beginMethod, Func < IAsyncResult , TResult >  endMethod, TArg1 arg1, TArg2 arg2, TArg3 arg3,  object  state,  TaskCreationOptions  creationOptions);

我们就以Stream类型的异步读取做例子,不用FromAsync的话这样写:

var  ms  =   new   MemoryStream ( new   byte [] {  1 ,  2 ,  3  });

byte [] data  =   new   byte [ 10 ];

ms . BeginRead(data,  0 , data . Length, ir  =>

    {

         var  read  =  ms . EndRead(ir);

         Console . WriteLine( BitConverter . ToString(data . Take(read) . ToArray()));

    },  null );

Console . ReadKey();

而使用FromAsync:

var  ms  =   new   MemoryStream ( new   byte [] {  1 ,  2 ,  3  });

byte [] data  =   new   byte [ 10 ];

var  task  =   Task < int >. Factory . FromAsync(ms . BeginRead, ms . EndRead, data,  0 , data . Length,  null )

     . ContinueWith(t  =>   Console . WriteLine( BitConverter . ToString(data . Take(t . Result) . ToArray())));

Console . ReadKey();

后者更精短。

:D

作者: Mgen

出处: www.cnblogs.com/mgen

本文版权归作者所有,欢迎以网址(链接)的方式转载,不欢迎复制文章内容的方式转载,其一是为了在搜索引擎中去掉重复文章内容,其二复制后的文章往往没有提供本博客的页面格式和链接,造成文章可读性很差。望有素质人自觉遵守上述建议。

如果一定要以复制文章内容的方式转载,必须在文章开头标明作者信息和原文章链接地址。否则保留追究法律责任的权利。

当前标签: BCL

共4页: 1  2   3   4   下一页  

.NET(C#) TPL:TaskFactory.FromAsync与委托的异步调用   _Mgen 2012-03-02 14:09 阅读:919 评论:0   

 

.NET(C#):设置文件系统对象的访问控制   _Mgen 2012-02-28 14:26 阅读:116 评论:0   

 

.NET(C#):线程本地存储(Thread-Local Storage)之ThreadStatic, LocalDataStoreSlot和ThreadLocal<T>   _Mgen 2012-02-24 13:23 阅读:1636 评论:7   

 

.NET(C#):ConcurrentBag<T>同线程元素的添加和删除   _Mgen 2012-02-21 13:45 阅读:27 评论:0   

 

.NET(C#) TPL:Task中未觉察异常和TaskScheduler.UnobservedTaskException事件   _Mgen 2012-02-20 22:21 阅读:31 评论:0   

 

.NET(C#) TPL:Task, Parallel, PLINQ中取消操作后的OperationCanceledException异常   _Mgen 2012-02-20 19:02 阅读:19 评论:0   

 

.NET(C#) TPL:Parallel循环和多个Task的异常处理   _Mgen 2012-02-20 13:40 阅读:24 评论:0   

 

.NET(C#):DebuggerDisplay特性碉堡了!   _Mgen 2012-02-19 11:33 阅读:30 评论:0   

 

.NET(C#) TPL:ParallelLoopState的Break方法和LowestBreakIteration属性   _Mgen 2012-02-18 20:53 阅读:22 评论:0   

 

.NET(C#):警惕PLINQ结果的无序性   _Mgen 2012-02-17 23:23 阅读:21 评论:0   

 

.NET(C#):DLR有趣的调用自己的动态对象   _Mgen 2012-02-17 20:28 阅读:21 评论:0   

 

.NET(C#):计算HttpWebResponse的下载速度   _Mgen 2012-01-30 16:34 阅读:84 评论:0   

 

.NET(C#):使用HttpWebRequest头中的Range下载文件片段   _Mgen 2012-01-30 10:38 阅读:88 评论:0   

 

.NET(C#):将数据字节大小转换成易读的单位字符串   _Mgen 2012-01-28 18:29 阅读:52 评论:0   

 

.NET(C#):很少被注意的Assembly.CreateQualifiedName和Type.ReflectionOnlyGetType方法   _Mgen 2012-01-08 16:32 阅读:36 评论:0   

 

WPF:处理程序的“未处理异常”   _Mgen 2012-01-07 23:38 阅读:58 评论:0   

 

.NET(C#):在KeyedCollection类型中加字典的TryGetValue方法   _Mgen 2012-01-07 18:31 阅读:59 评论:2   

 

[我的软件]Mgen 轻型任务管理器 1.0   _Mgen 2012-01-04 20:21 阅读:1617 评论:16   

 

Mgen.BasicTask V2   _Mgen 2012-01-04 16:52 阅读:51 评论:0   

 

Mgen.BasicTask V1   _Mgen 2012-01-01 20:09 阅读:178 评论:0   

 

.NET(C#):监控CPU和内存的使用状况   _Mgen 2011-12-28 18:17 阅读:411 评论:3   

 

[我的小软件]Mgen.TXT转EXE   _Mgen 2011-12-26 23:07 阅读:1338 评论:6   

 

.NET(C#):关于部属程序集时的重定向版本   _Mgen 2011-12-25 17:20 阅读:68 评论:0   

 

.NET(C#):关于正确读取中文文本文件   _Mgen 2011-12-25 13:40 阅读:304 评论:0   

 

.NET(C#):正则表达式的RegexOptions和行数   _Mgen 2011-12-24 22:20 阅读:63 评论:0   

 

.NET(C#):信号量(Semaphore)的初始请求数和最大请求数   _Mgen 2011-12-24 19:49 阅读:46 评论:0   

 

.NET(C#):从文件中觉察编码   _Mgen 2011-12-23 00:24 阅读:65 评论:0   

 

.NET(C#):两个进程防止被终止   _Mgen 2011-12-21 23:15 阅读:62 评论:0   

 

.NET(C#):关于进程退出的事件   _Mgen 2011-12-19 00:06 阅读:79 评论:0   

 

.NET(C#):在文件访问控制列表中加入审核规则(Audit Rule)   _Mgen 2011-12-18 17:17 阅读:39 评论:0   

 

.NET(C#):不使用SMTP的25端口发邮件的权限检查   _Mgen 2011-12-17 15:54 阅读:42 评论:0   

 

.NET(C#):通过WindowsPrincipal和WindowsIdentity.Groups判断用户组   _Mgen 2011-12-17 15:36 阅读:86 评论:0   

 

.NET(C#):反射Emit生成WPF程序   _Mgen 2011-12-16 18:45 阅读:110 评论:0   

 

.NET(C#):反射Emit生成结构体   _Mgen 2011-12-16 16:30 阅读:38 评论:0   

 

.NET(C#):用代码来添加断点并且在Visual Studio输出窗口中显示自定义信息   _Mgen 2011-12-16 00:33 阅读:197 评论:0   

 

.NET(C#):使用FileVersionInfo类做一个简单的文件信息查看器   _Mgen 2011-12-15 23:22 阅读:190 评论:2   

 

.NET(C#):SecurityAction.RequestMinimum和RequestOptional   _Mgen 2011-12-15 18:06 阅读:22 评论:0   

 

.NET(C#):网络域中的计算机的WMI查询   _Mgen 2011-12-15 17:16 阅读:71 评论:0   

 

.NET(C#):ComImport特性和ComVisible特性   _Mgen 2011-12-14 23:11 阅读:92 评论:0   

 

.NET(C#):只有ReflectionOnlyLoadFrom才可以拯救与GAC冲突的强命名程序集   _Mgen 2011-12-13 18:09 阅读:27 评论:0   

 

.NET(C#):有趣的IdentityReference和WindowsIdentity   _Mgen 2011-12-13 17:19 阅读:41 评论:0   

 

.NET(C#):使用ObjectSecurity.SetAccessRuleProtection保留访问控制数据   _Mgen 2011-12-13 16:53 阅读:18 评论:0   

 

.NET(C#):程序集配置的<codebase>元素和<probing>元素   _Mgen 2011-12-12 21:45 阅读:95 评论:0   

 

.NET(C#):使用BinaryFormatter.UnsafeDeserialize增加反序列化性能   _Mgen 2011-12-12 12:25 阅读:49 评论:0   

 

.NET(C#):觉察XML反序列化中的未知节点   _Mgen 2011-12-12 11:16 阅读:35 评论:0   

 

.NET(C#):使用ManagementEventWatcher进行WMI事件查询   _Mgen 2011-12-12 10:29 阅读:72 评论:2   

 

.NET(C#):ManagementQuery类型构建WMI中的WQL选择查询和事件查询   _Mgen 2011-12-12 09:50 阅读:41 评论:0   

 

.NET(C#):浅谈程序集清单资源和RESX资源   _Mgen 2011-12-11 14:08 阅读:1201 评论:2   

 

.NET(C#):使用ResourceManager类型   _Mgen 2011-12-11 13:16 阅读:241 评论:0   

 

.NET(C#):使用IResourceReader,IResourceWriter和ResourceSet   _Mgen 2011-12-11 00:56 阅读:62 评论:0   

 

共4页: 1  2   3   4   下一页  

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于.NET(C#) TPL:TaskFactory.FromAsync与委托的异步调用的详细内容...

  阅读:45次