C#学习笔记 异步操作
2016-01-09 23:29
253 查看
同步操作
默认情况下我们的代码都是同步操作。这种情况下,所有的操作都在同一个线程中,如果遇到需要长时间执行的操作或者是一个IO操作,那么代码可能会阻塞比较长的时间。在阻塞的这段时间里,无法进行其他工作,这是很不好的。这里是一个同步操作的例子。一个操作需要大约5秒时间,然后另一个操作好过去前一个操作的结果并显示。在这5秒钟时间里,线程会被阻塞,无法进行任何工作。
private static string DoSomethingLong() { Console.WriteLine("5秒钟之后完成...请等待"); Thread.Sleep(5000); return "abc"; } private static void DoSomethingElse(string str) { Console.WriteLine("结果是:" + str); } public static void DoSynchronousWork() { Console.WriteLine("同步地执行方法:"); string data = DoSomethingLong(); Console.WriteLine("等待结果:"); DoSomethingElse(data); Console.WriteLine(); }
异步操作
.NET支持三种类型的异步操作方式,第一种是异步模式。实现这种模式的类会定义类似BeginXXX和EndXXX的方法。现在这种方式已经不怎么使用了。第二种是基于事件的异步模式,实现这种模式的类会定义一个事件,该事件会在异步调用完成之后被触发,我们要做的事情就是向这个事件注册一个监听程序即可。第三种就是现在最新的基于任务的异步模式,这种方式利用了类库中已有的Task类和async/await关键字来实现,现在主要就是使用这种模式。异步方法
要使用基于任务的异步模式,首先需要一个返回Task的方法。private static Task<string> DoSomethingLongAsync() { return Task.Run(() => DoSomethingLong()); }
返回Task的方法被认为是可等待的。这样的方法可以使用
await关键字等待。如果一个方法体中使用了
await关键字,那么这个方法声明就必须添加
async关键字。添加了
async关键字的方法就是异步方法。异步方法在执行的时候遇到
await关键字处,不会被阻塞,而是直接返回,等到
await出的代码执行完毕,一个线程就会进入这里继续执行。这里所有的额外行为全部由编译器实现。
public static async Task DoWorkUseAsyncAndAwait() { Console.WriteLine("利用async和await关键字异步地执行方法:"); string data = await DoSomethingLongAsync(); Console.WriteLine("等待结果:"); DoSomethingElse(data); Console.WriteLine(); }
上面这个方法工作起来就像下面一样:
public static void DoWorkAsynchronouslyWithTask() { Console.WriteLine("利用任务异步地执行方法:"); Task<string> something = Task.Run(() => DoSomethingLong()); Console.WriteLine("等待结果:"); Task somethingElse = something.ContinueWith(task => DoSomethingElse(task.Result)); somethingElse.Wait(); Console.WriteLine(); }
异步方法的异常处理
一般情况下使用Task的时候如果抛出异常,Task会抛出一个AggregateException异常,内部的InnerException和InnerExceptions属性会封装了实际抛出的异常。而使用异步方法的时候,为了提供与同步方法相似的编程体验,当抛出异常的时候会直接抛出原始异常而不是AggregateException异常。首先先来定义一个返回Task的会抛出异常的方法。
private static Task<string> DoSomethingLongWithException() { Func<string> action = () => { Console.WriteLine("5秒之后完成..."); Thread.Sleep(5000); if (5 > 0) throw new InvalidOperationException("在长时间运行时发生了无效的操作"); return "123"; }; return Task.Run(action); }
然后异步等待这个方法,并处理可能抛出的异常:
public static async Task WorkWithSomethingThrowable() { Console.WriteLine("开始执行可能抛出异常的异步方法:"); string result = null; try { result = await DoSomethingLongWithException(); Console.WriteLine($"执行的结果是:{result}"); } catch (InvalidOperationException ex) { Console.WriteLine($"捕获到了异常:{ex.Message}"); } Console.WriteLine(); }
如果异步方法抛出了异常,那么代表这个异步方法的Task对象会因为异常而结束,等待这个异步方法的代码就会获得该异常。但是如果抛出异常的异步方法返回void,调用者就无法捕获该异常。这个时候,编译器生成的代码会捕捉它,并在调用者的同步上下文上重新抛出异常,这会导致整个程序结束。所以,尽量使用返回Task的异步方法。
相关文章推荐
- ruby实现的一个异步文件下载HttpServer实例
- C#异步绑定数据实现方法
- C#线程间不能调用剪切板的解决方法
- 科学知识:同步、异步、阻塞和非阻塞区别
- 探讨Ajax中同步与异步之间的区别
- C#线程同步的三类情景分析
- C#获取进程或线程相关信息的方法
- C#停止线程的方法
- C#子线程更新UI控件的方法实例总结
- C#线程队列用法实例分析
- C#中异步回调函数用法实例
- C++使用CriticalSection实现线程同步实例
- 基于C++实现的线程休眠代码
- C#实现异步GET的方法
- C#异步下载文件
- VB读取线程、句柄及写入内存的API代码实例
- C#网络编程基础之进程和线程详解
- C#通过Semaphore类控制线程队列的方法
- C#异步执行任务的方法
- C#多线程处理多个队列数据的方法