【问题标题】:does last async call in async method require await?异步方法中的最后一个异步调用是否需要等待?
【发布时间】:2013-12-15 17:58:16
【问题描述】:

当异步方法调用是我的异步方法中的最后一次调用时,我不明白是否需要等待语句。例如

public async Task CallAsync(byte[] data)
{
    await Call1Async(data);
    Call2Async(data);
}

public async Task Call1Async(byte[] data)
{
   ...
}

public async Task Call2Async(byte[] data)
{
   ...
}

以上内容可以编译,但会出现警告“考虑将 await 应用于此方法”。但是我认为在方法的最后一次调用中应用 await 会浪费资源。

同时,如果我使用return Call2Async(data); 而不仅仅是Call2Async(data);,警告就会消失。这表明在这种情况下实际上不需要await。

但这仅适用于具有返回值的方法(即Task<T> 而不仅仅是Task)。对于没有返回值的方法,我需要这个。有没有办法对返回值的方法做同样的事情?

即我需要以某种方式将Call2Async 返回的Task 对象返回给CallAsync 的调用者,并且我不能在CallAsync 中明确使用return 语句,因为我的方法没有返回值。当然,我可以将它们全部调整为返回值(例如 true),但这不是一个优雅的解决方案。

附:如果我的方法中有唯一的异步调用,我只是不将“异步”添加到方法签名中,并且它可以工作(不需要“返回”语句)。但是如果该方法包含多个异步调用,我需要有“异步”修饰符才能等待第一次调用“等待 Call1Async(数据)”。添加'async'修饰符使编译器需要'return'将Task对象返回给调用者(这仅适用于具有返回值的方法)。这就是我想要克服的。

【问题讨论】:

    标签: c# .net asynchronous


    【解决方案1】:

    在以下方法中:

    public async Task CallAsync(byte[] data)
    {
        await Call1Async(data);
        Call2Async(data);
    }
    

    方法的控制在开始Call2Async 后返回。也就是说,如果您要去await CallAsync(data),它将在Call2Async 结束之前完成。这可能不是您想要的。该方法只有在两个调用都完成时才会结束:

    public async Task CallAsync(byte[] data)
    {
        await Call1Async(data);
        await Call2Async(data);
    }
    

    您可以在这些示例输出中看到差异(我实现了两个异步调用来简单地打印开始、延迟一秒钟,然后打印结束)。

    void Main()
    {
        Console.WriteLine("Main start");
        CallAsync(null).Wait();
        Console.WriteLine("Main end");
    }
    

    没有第二个await的方法打印:

    Main start
    Call1Async start
    Call1Async end
    Call2Async start
    Main end
    Call2Async end
    

    还有第二个await:

    Main start
    Call1Async start
    Call1Async end
    Call2Async start
    Call2Async end
    Main end
    

    这个实现也有问题:

    public Task CallAsync(byte[] data)
    {
        Call1Async(data);
        return Call2Async(data);
    }
    

    它同时启动两个异步调用,然后在Call2Async 完成时返回控制权,无论Call1Async 是否完成。同样,这可能不是您想要的。

    【讨论】:

    • @dcastro 除非您将CallAsync 的类型设为Task<Task>,否则您不能这样做: 1. 您包含async 关键字并且可以使用await,但必须另外使用把方法写成void,或者2.你没有async,不能用await,然后返回一个Task
    • @TimS。谢谢你的答复。因此,您确认从 CallAsync 向调用者返回 Call2Async 返回的相同任务的唯一方法是使所有任务参数化,例如 Task 即使我不需要任何返回值?
    • @Alex 通常不需要包含虚拟值或返回Call2Async 的Task。我建议您只保留它们Task 并同时使用awaits。您(很可能)不想返回Call2Async 的Task,而是让async 生成一个Task,等待两个异步调用完成。
    • @Alex 如果您返回了Call2Async 的Task,那么您对CallAsync 的调用就不能等待Call1Async 的Task。它可以关闭它并忘记它,但不能等待它。这就是为什么您希望它拥有自己的Task。
    • 谢谢。我只是想避免通过 await 生成更多任务的额外开销,但它的利润很可能不仅仅是值得花时间和增加代码的复杂性。
    【解决方案2】:

    async 关键字表示该方法是异步的,并且其中将包含 await 关键字。 我不明白你为什么有多个异步方法?

    考虑以下示例:

    async void MyMethod()
    {
    Task<object> result = MyWorkAsync();
    
    MyIndependentOtherWork();
    
    object res = await result;
    }
    

    调用 MyWorkAsync 就像创建一个线程“toto”来执行方法(即 Thread.Start(MyWorkAsyn))

    await 关键字就像是从当前线程连接到线程“toto”而不是获得“toto”的结果,同时您可以在当前线程上执行其他独立工作

    【讨论】:

    • “加入”在这里是错误的术语,它不是“加入”(加入意味着一个线程等待另一个线程)它添加了一个延续。
    • 这些异步方法在内部调用异步套接字。例如。 Call1Async 发送请求,Call2Async 接收响应。
    • 没错Peter,选词错误,await挂起方法执行不是当前线程
    猜你喜欢
    • 1970-01-01
    • 2020-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-23
    相关资源
    最近更新 更多