在此特定场景中将 await 放在异步方法中的什么位置

问题描述 投票:0回答:4

我和一个同事讨论这个,我们都不确定。鉴于这两种方法使用 C# 中的异步框架,两者之间的性能会有所不同吗?

public async Task<ReadRespone> MethodA(ReadRequest request)
{
    var response = SynchronousReadThatTakesAWhileToExecute();
    return await Task.FromResult(new ReadResponse(response));
}

public async Task<ReadResponse> MethodB(ReadRequest request)
{
    var response = await Task.FromResult(SynchronousReadThatTakesAWhileToExecute());
    return new ReadResponse(response);
}

我在想 MethodA 会同步执行慢速方法,然后,因为它只使用 await 作为一个普通的构造函数,实际上是一个同步方法,它会阻止其他线程的执行。我的同事认为两者在实践中是一样的,异步方法应该总是返回一个任务(但我认为 await 已经“拆箱”了任务,并且异步方法只需要在某处有一个等待任务在里面是正确的)。

谢谢

c# multithreading asynchronous async-await concurrency
4个回答
3
投票

你的同事是对的。

MethodA
MethodB
实际上是等价的。它们都是具有同步实现的异步外观。每次调用这些方法时,调用线程完成所有工作,然后交还一个已经完成的任务 (
.IsCompleted == true
)。任务包含结果或异常。
async
关键字仅有助于简化结果/异常包装。您可以在没有
async
的情况下实现完全相同的行为,但需要多一点代码:

public Task<ReadResponse> MethodC(ReadRequest request)
{
    try
    {
        var response = SynchronousReadThatTakesAWhileToExecute();
        return Task.FromResult(new ReadResponse(response));
    }
    catch (OperationCanceledException oce)
    {
        return Task.FromCanceled<ReadResponse>(oce.CancellationToken);
    }
    catch (Exception ex)
    {
        return Task.FromException<ReadResponse>(ex);
    }
}

async
方法
MethodC
用于教育/演示目的(它有一个隐藏的包)。在实践中,最好使用
async
进行包装。


2
投票

正如其他人指出的那样,

Task.FromResult()
并不是那样工作的。它接受一个值并返回一个已完成的任务和该值。你想使用
Task.Run
TaskFactory.StartNew
.

public async Task<ReadResponse> Foo(ReadRequest request)
{
    var result = await Task.Run(SynchronousReadThatTakesAWhileToExecute);
    return new ReadResponse(result);
}

注意

SynchronousReadThatTakesAWhileToExecute
后没有括号。这是因为我没有直接调用该函数,而是将其传递给
Task.Run
。这在功能上等同于将
() => SynchronousReadThatTakesAWhileToExecute()
作为参数传递给
Task.Run

编辑我的回复,使其真正回答问题: 提供的两个选项在性能方面几乎相同。


2
投票

基本相同。两者都将执行阻塞工作而不将控制权交还给调用方法。 异步状态机如果任务已经完成,编译器生成的将“快捷方式”,实际上将继续执行当前方法。这可以用以下代码演示:

async Task FakeAsync()
{
    Console.WriteLine("Before IN FakeAsync");
    await Task.CompletedTask;
    Thread.Sleep(500); // "simulate" CPU-bound work
    Console.WriteLine("After IN FakeAsync");
}

async Task RealAsync()
{
    Console.WriteLine("Before IN RealAsync");
    await Task.Yield();
    Thread.Sleep(500); // "simulate" CPU-bound work
    Console.WriteLine("After IN RealAsync");
}

Console.WriteLine("Before FakeAsync");
var task =  FakeAsync();
Console.WriteLine("After FakeAsync");
await task;
Console.WriteLine("After await FakeAsync");
Console.WriteLine("---------");
Console.WriteLine("Before RealAsync");
var task1 = RealAsync();
Console.WriteLine("After RealAsync");
await task1;
Console.WriteLine("After await RealAsync");

这将给出以下输出:

Before FakeAsync
Before IN FakeAsync
After IN FakeAsync
After FakeAsync
After await FakeAsync
---------
Before RealAsync
Before IN RealAsync
After RealAsync
After IN RealAsync
After await RealAsync

请注意,对于

FakeAsync
,两个“IN”语句都在调用方法的其余部分之前执行,而对于
RealAsync
,控制权返回给调用方法(因此“IN”之间的“After RealAsync” s)

同时请记住,

Task.Yield
技巧在不存在同步上下文时会起作用,因此它可以用于某些测试场景,对于“现实生活”,更好的方法是
Task.Run

async Task RealAsync()
{
    Console.WriteLine("Before IN RealAsync");
    await Task.Run(() => Thread.Sleep(500)); // "simulate" CPU-bound work)
    Console.WriteLine("After IN RealAsync");
}

阅读更多:


0
投票

正如其他人所指出的,这些方法之间几乎没有区别。

然而,这两种方法都是完全同步的,即使它们具有异步签名。我不推荐这样做,因为使用这段代码的任何人都会感到困惑。

如果你可以让它异步,那么就这样做:

public async Task<ReadRespone> MethodAsync(ReadRequest request)
{
  var response = await AsynchronousReadThatTakesAWhileToExecuteAsync();
  return new ReadResponse(response);
}

如果工作只是同步的(你不能让它异步),那么让你的方法同步:

public ReadRespone Method(ReadRequest request)
{
  var response = SynchronousReadThatTakesAWhileToExecute();
  return new ReadResponse(response);
}

如果你有同步工作(你不能让它异步),并且你的方法必须是异步的(即它正在实现一个接口方法),那么你可以同步实现一个异步接口。请注意,此代码同步运行,这让消费者感到惊讶,因此我建议在接口文档本身中记录可能同步的性质。

#pragma warning disable 1998
public async Task<ReadRespone> MethodAsync(ReadRequest request)
#pragma warning restore 1998
{
  var response = SynchronousReadThatTakesAWhileToExecute();
  return new ReadResponse(response);
}
© www.soinside.com 2019 - 2024. All rights reserved.