前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#学习笔记 异步操作

C#学习笔记 异步操作

作者头像
乐百川
发布2022-05-05 18:59:08
4660
发布2022-05-05 18:59:08
举报

同步操作

默认情况下我们的代码都是同步操作。这种情况下,所有的操作都在同一个线程中,如果遇到需要长时间执行的操作或者是一个IO操作,那么代码可能会阻塞比较长的时间。在阻塞的这段时间里,无法进行其他工作,这是很不好的。

这里是一个同步操作的例子。一个操作需要大约5秒时间,然后另一个操作好过去前一个操作的结果并显示。在这5秒钟时间里,线程会被阻塞,无法进行任何工作。

代码语言:javascript
复制
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的方法。

代码语言:javascript
复制
private static Task<string> DoSomethingLongAsync()
{
    return Task.Run(() => DoSomethingLong());
}

返回Task的方法被认为是可等待的。这样的方法可以使用await关键字等待。如果一个方法体中使用了await关键字,那么这个方法声明就必须添加async关键字。添加了async关键字的方法就是异步方法。异步方法在执行的时候遇到await关键字处,不会被阻塞,而是直接返回,等到await出的代码执行完毕,一个线程就会进入这里继续执行。这里所有的额外行为全部由编译器实现。

代码语言:javascript
复制
public static async Task DoWorkUseAsyncAndAwait()
{
    Console.WriteLine("利用async和await关键字异步地执行方法:");
    string data = await DoSomethingLongAsync();
    Console.WriteLine("等待结果:");
    DoSomethingElse(data);

    Console.WriteLine();
}

上面这个方法工作起来就像下面一样:

代码语言:javascript
复制
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的会抛出异常的方法。

代码语言:javascript
复制
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);
}

然后异步等待这个方法,并处理可能抛出的异常:

代码语言:javascript
复制
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的异步方法。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-01-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 同步操作
  • 异步操作
    • 异步方法
      • 异步方法的异常处理
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档