异步等待性能?

Roy*_*mir 28 c# theory performance async-await

(只是一个理论问题 - 对于非gui应用程序)

假设我有很多这样的代码awaits:

public async Task<T> ConsumeAsync()
    {
          await A();
          await b();
          await c();
          await d();
          //..
    }
Run Code Online (Sandbox Code Playgroud)

每项任务可能需要很短的时间,

问题(再次,理论)

可能是 其中的一个情况总体时间处理所有那些"释放回线",在这里"获取线程回"(红色和绿色:)

在此输入图像描述

花费的时间比单个线程花费更多的时间,可以通过少量延迟完成所有工作,

我的意思是,我想成为最富有成效的人,但相反,因为所有这些来回切换 - 我实际上失去了生产力.

这种情况会发生吗?

Ste*_*ary 20

是的,从理论上讲.通常情况下,在现实世界中.

在通常情况下,async用于I/O绑定操作,与它们相比,线程管理的开销是不可检测的.大多数情况下,异步操作要么花费很长时间(与线程管理相比),要么已经完成(例如,缓存).注意,async如果操作已经完成,那么它就会启动一个"快速路径",它不会产生线程.

有关更多信息,请参阅AsyncAsync PerformanceZen.

  • 使用快速路径的@jgauffin`await`代码只使用少量的TPL(相当于`Task.FromResult()`和`Task.Result`).你是这个意思吗?它肯定没有做类似于`Task.Run()`或类似的任何事情. (5认同)

nos*_*tio 14

Task对象代表一个挂起的操作的延迟的结果.async/await如果您没有任何待处理操作,则不必使用任务.否则,我相信async/ await代码通常比其裸TPL ContinueWith模拟更有效.

我们来做一些时间:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    class Program
    {
        // async/await version
        static async Task<int> Test1Async(Task<int> task)
        {
            return await task;
        }

        // TPL version
        static Task<int> Test2Async(Task<int> task)
        {
            return task.ContinueWith(
                t => t.Result,
                CancellationToken.None,
                TaskContinuationOptions.ExecuteSynchronously,
                TaskScheduler.Default);
        }

        static void Tester(string name, Func<Task<int>, Task<int>> func)
        {
            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            for (int i = 0; i < 10000000; i++)
            {
                func(Task.FromResult(0)).Wait();
            }
            sw.Stop();
            Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
        }

        static void Main(string[] args)
        {
            Tester("Test1Async", Test1Async);
            Tester("Test2Async", Test2Async);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Test1Async: 1582ms
Test2Async: 4975ms

因此,默认情况下,await延续处理比ContinueWith延续处理更有效.让我们稍微优化一下这段代码:

// async/await version
static async Task<int> Test1Async(Task<int> task)
{
    if (task.IsCompleted)
        return task.Result;
    return await task;
}

// TPL version
static Task<int> Test2Async(Task<int> task)
{
    if (task.IsCompleted)
        return Task.FromResult(task.Result);

    return task.ContinueWith(
        t => t.Result,
        CancellationToken.None,
        TaskContinuationOptions.ExecuteSynchronously,
        TaskScheduler.Default);
}
Run Code Online (Sandbox Code Playgroud)

输出:

Test1Async: 1557ms
Test2Async: 429ms

现在,非异步版本获胜.对于async版本,我相信这种优化已经由async/await基础设施内部完成.

无论如何,到目前为止,我们只完成了已完成的任务(Task.FromResult).让我们介绍一下实际的异步(当然,这次我们会做更少的迭代):

static Task<int> DoAsync()
{
    var tcs = new TaskCompletionSource<int>();
    ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(0));
    return tcs.Task;
}

static void Tester(string name, Func<Task<int>, Task<int>> func)
{
    ThreadPool.SetMinThreads(200, 200);
    var sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    for (int i = 0; i < 1000000; i++)
    {
        func(DoAsync()).Wait();
    }
    sw.Stop();
    Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
}
Run Code Online (Sandbox Code Playgroud)

输出:

Test1Async: 4207ms
Test2Async: 4734ms

现在差异非常小,尽管async版本仍然表现稍好一些.然而,我认为这种收益实际上是可以忽略的,与异步操作的实际成本或恢复捕获的上下文的成本相当SynchronizationContext.Current != null.

最重要的是,如果您处理异步任务,请选择async/ await如果您有选择,不是出于性能原因而是为了易用性,可读性和可维护性.