Result、ConfigAwait、ValueTask

参照:

C# Async/Await: ConfigAwait, ValueTask是个啥?对提高性能有用么?_哔哩哔哩_bilibili

理解ValueTask - 知乎 (zhihu.com)
Demo:https://files.cnblogs.com/files/summerZoo/AsyncAwaitAdvance.zip?t=1647483629

  1. Result

定义:

1、访问属性的 get 访问器会使当前线程等待(阻塞当前线程),直到异步操作完成;

2、效果像调用 Wait一样当前线程等待异步线程执行完,可用GetAwaiter().GetResult()替代

3、操作结果可用后,将存储该结果,在后续调用 属性时立即 Result 返回。若发生没有返回值的异常采用 AggregateException 捕获。

public TResult Result { get; }

用处:可用于在同步方法里用同步的方式调用异步方法

但是对于程序性能的考虑尽量不要采用这种方式,在winform里用Result会卡死调用的UI线程,可能还会因为在winform默认ConfigAwait为true的原因形成死锁

尽量采用异步async/await的方式去提高程序的性能。

例子:

class Program
    {
        static void Main(string[] args)
        {
           AsyncNoReturn().Wait();
           int s = AsyncMethod().GetAwaiter().GetResult();
           int m = AsyncMethod().Result;
        }
        public static async Task<int> AsyncMethod()
        {
            await Task.Delay(500);
            return 22;
        }
        public static async Task AsyncNoReturn()
        {
            await Task.Delay(500);
        }

    }
  1. ConfigAwait

定义:配置用于等待此 Task的 awaiter

如下图若配置flag = true,则RunSomethingAsync在执行完毕后,返回原来线程ID为1的线程继续执行After Await的代码部分;

若配置flag = false,则RunSomethingAsync在执行完毕后,从线程池随机返回一个空闲的线程执行After Await的代码部分;

        public  async Task RunTask()
        {
            // Before Await;线程ID:1
            await RunSomethingAsync().ConfigAwait(flag);
            // After Await;
        }

应用:

在Winform、WPF里,基本上ConfigAwait默认设置为true,因为在程序里有大量需要UI线程才能执行的操作,After Await里有相关UI的操作,ConfigAwait配置成false,在完成RunSomethingAsync()后,随机从线程池拿一个空闲线程来继续往下执行After Await,若这个线程不是原有的UI线程则会出现异常。

除去上述情况下,我们把ConfigAwait设置成false,不要求在原来线程上继续执行可提高性能。

  1. ValueTask

ValueTask的数据结构比Task的数据结构小,在合适的场景下可使用替代Task,降低内存分配开销。

从本质上来讲ValueTask是一个结构体,Task是一个类。

在返回信息比较少的场景,比如Task、Task场景可以使用ValueTask、ValueTask来代替

适合场景:

1、运行吞吐量非常高的服务时,我们仍然关心怎么尽可能多地避免内存分配

使用条件:

  1. 不需要多次await task
  2. 在非并发await场景(比如和WhenAny、WhenAll、ContinueWhenAll等组合器一起使用处理并发的场景)
  3. 在Async方法被异步调用时,比如AsyncMethod().GetAwaiter().GetResult()、AsyncMethod().Result就不适合