【问题标题】:Performance impact of using async await when its not necessary不必要时使用异步等待的性能影响
【发布时间】:2017-01-25 11:14:07
【问题描述】:

假设我有这些方法:

public async Task<Something> GetSomethingAsync()
{
    var somethingService = new SomethingService();

    return await service.GetAsync(); 
}

public Task<Something> GetSomethingAsync()
{
    var somethingService = new SomethingService();

    return service.GetAsync();  
}

两个选项的编译和工作方式相同。如果一个选项比另一个更快,是否有任何最佳做法?

还是只是一些语法糖?

【问题讨论】:

  • 第一个选项等待任务完成,但第二个没有。所以第二个会更快。
  • @Enigmativity,两种方法都会同时返回给调用者。但在第二种方法中,“等待”任务的责任将转移给调用者。而在第一种方法中,代码执行将在任务完成时返回。
  • @Enigmativity 不,两者都返回具有相同行为的任务
  • @Fabio 不,调用者从两者返回完全相同的东西:Task&lt;Something&gt;

标签: c# asynchronous async-await


【解决方案1】:

在第一种方法中,编译器将围绕它生成“状态机”代码,并在任务完成后执行将返回到行return await service.GetAsync();。考虑下面的例子:

public async Task<Something> GetSomethingAsync()
{
    var somethingService = new SomethingService();

    // Here execution returns to the caller and returned back only when Task is completed.
    Something value = await service.GetAsync(); 

    DoSomething();

    return value;        
}

DoSomething(); 行只有在service.GetAsync 任务完成后才会执行。

第二种方法只是开始执行service.GetAsync并将对应的Task返回给调用者而不等待完成。

public Task<Something> GetSomethingAsync()
{
    var somethingService = new SomethingService();

    Task<Something> valueTask = service.GetAsync(); 

    DoSomething();

    return valueTask;        
}

所以在上面的例子中DoSomething() 将在Task&lt;Something&gt; valueTask = service.GetAsync(); 行之后直接执行而不等待任务完成。

在另一个线程上执行async 方法取决于方法本身。

如果方法执行IO操作,那么另一个线程只会浪费线程,它什么也不做,只等待响应。我认为async - awaitIO 操作的完美方法。

如果方法GetAsync 包含例如Task.Run,则执行转到从线程池中获取的另一个线程。

下面是一个简短的例子,不是一个很好的例子,但它显示了一个试图解释的逻辑:

static async Task GetAsync()
{
    for(int i = 0; i < 10; i++)
    {
        Console.WriteLine($"Iterate GetAsync: {i}");
        await Task.Delay(500);
    }
}

static Task GetSomethingAsync() => GetAsync();

static void Main(string[] args)
{
    Task gettingSomethingTask = GetSomethingAsync();

    Console.WriteLine("GetAsync Task returned");

    Console.WriteLine("Start sleeping");            
    Thread.Sleep(3000);            
    Console.WriteLine("End sleeping");

    Console.WriteLine("Before Task awaiting");
    gettingSomethingTask.Wait();
    Console.WriteLine("After Task awaited");

    Console.ReadLine();
}

接下来是输出:

Iterate GetAsync: 0
GetAsync Task returned
Start sleeping
Iterate GetAsync: 1
Iterate GetAsync: 2
Iterate GetAsync: 3
Iterate GetAsync: 4
Iterate GetAsync: 5
End sleeping
Before Task awaiting
Iterate GetAsync: 6
Iterate GetAsync: 7
Iterate GetAsync: 8
Iterate GetAsync: 9
After Task awaited

正如您所见,GetAsync 的执行在调用它之后立即开始。

如果GetSomethingAsync() 将更改为:

static Task GetSomethingAsync() => new Task(async () => await GetAsync());

如果GetAsync 包裹在另一个任务中,那么GetAsync() 将根本不会执行,输出将是:

GetAsync Task returned
Start sleeping
End sleeping
Before Task awaiting
After Task awaited

当然,您需要删除行gettingSomethingTask.Wait();,因为那时应用程序只是等待甚至没有启动的任务。

【讨论】:

  • 谢谢!这似乎是有道理的:)不过有一条评论-您的第二种方法中的“异步”根本没有必要,因为您没有在正文的任何​​地方调用“等待”。
  • 我错过了什么吗?我想;在第二个示例中,valueTask 根本没有执行。直到你 awaitvalueTask.Start() 等..
  • 这也是一个好点。我真的被这一切弄糊涂了。我以为这很简单。
  • @JeroenvanLangen,正如我在返回的Task 的回答行为中提到的,取决于GetAsync 中的代码。
猜你喜欢
  • 2014-06-16
  • 1970-01-01
  • 2014-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-03
  • 1970-01-01
相关资源
最近更新 更多