等待异步任务函数和在void函数中调用await之间有什么区别?

时间:2016-07-14 07:41:07

标签: c# .net asynchronous async-await task

要理解这个问题,请查看await调用以及以下示例的函数InitSyncContext()的定义。

基于此我想知道程序将如何在每个场景中表现,因为我不完全理解调用await InitSyncContext(store)和在没有返回的情况下调用await之间的区别Task

作为参考,我之前做了一项研究,我发现了一个类似的example here,但我认为这与我的情况有所不同。

*以下代码是真实世界代码的简化示例,仅用于演示目的。

void Main()
{
    Initializer();
}

private async void Initializer()
{
    var store = InitLocalStore();
    await InitSyncContext(store); // <-- Here, await call
    InitFileSync(store);
}

// Here returns Task (without having a return inside. No compile errors)
private async Task InitSyncContext(MobileServiceSQLiteStore store)
{
    await Client.SyncContext.InitializeAsync(store);
}

//------------- Second Method -------------

void Main()
{
    Initializer();
}

private void Initializer()
{
    var store = InitLocalStore();
    InitSyncContext(store); // <-- Here without await call
    InitFileSync(store);
}

// Here is void but with a async call inside
private async void InitSyncContext(MobileServiceSQLiteStore store)
{
    await Client.SyncContext.InitializeAsync(store);
}

3 个答案:

答案 0 :(得分:4)

  

等待异步任务函数和在void函数内调用await之间的区别是什么?

阅读Stephen Cleary先生的精美文章 Async/Await - Best Practices in Asynchronous Programming ,这可能是一个非常大的问题。

一些摘要点:

  

避免异步void - 优先于async void方法的异步任务方法

enter image description here

另见

答案 1 :(得分:3)

  

async Task函数中等待await函数和调用void之间的区别是什么?

全世界!一切......然后是一些。让我们退后一步,从一些基础知识开始。

你应该一直&#34;异步#34;,即;如果您要返回等待TaskTask<T>),则应正确使用asyncawait个关键字。

这里有几件事需要澄清。您不应该有async void个方法,而应该有async Task - 例外是事件处理程序(其中委托被预定义为非任务返回)等等。让我们检查并挑选方法一(我将忽略Main入口点):

<强>一

private async void Initializer()
{
    var store = InitLocalStore();
    await InitSyncContext(store); // <-- Here, await call
    ...
}

// Here returns Task (without having a return inside. No compile errors)
private async Task InitSyncContext(MobileServiceSQLiteStore store)
{
    await Client.SyncContext.InitializeAsync(store);
}

您有一个Initializer方法,它立即代码为async void。这应该是async Task,它应该有&#34; Async&#34;后缀。此外,您有InitSyncContext接受store变量并调用一些客户端初始化工作。这里的代码味道是您使用asyncawait。这不需要像这样的简单(单任务)工作负载。相反,您应该只使用return关键字。最底层的例子。让我们看一下方法二:

<强>两个

private void Initializer()
{
    var store = InitLocalStore();
    InitSyncContext(store); // <-- Here without await call
    ...
}

// Here is void but with a async call inside
private async void InitSyncContext(MobileServiceSQLiteStore store)
{
    await Client.SyncContext.InitializeAsync(store);
}

事情正式变得越来越糟糕!由于对异步命名法的误解,我们假设由于一种方法似乎可以正常工作&#34; ok&#34;没有考虑另一种方法可能效仿的最佳实践。您制作了InitSyncContext方法async void。方法不应该是async void的原因是它们是永远不会发生的。内部异步状态机没有Task锁定,因此状态丢失。当您删除来电者await时,您说要启动此异步操作,但您不关心它的结果。

以下是实现所需功能的正确方法:

<强>正确

private async Task InitializerAsync()
{
    var store = InitLocalStore();
    await InitSyncContextAsync(store);
    ...
}

// Simply return the task that represents the async operation and let the caller await it
private Task InitSyncContextAsync(MobileServiceSQLiteStore store)
{
    return Client.SyncContext.InitializeAsync(store);
}

关于代码段Main方法的说明,如果这是控制台应用程序的一部分 - 进入异步堆栈的顶级入口点将调用.Result或{{ 1}}。

进一步阅读

答案 2 :(得分:-1)

在第一个示例中,编译器生成如下内容:

TheadPool.RunTask(()=>InitSyncContext(store)).ContinueWith(()=>InitFileSync(store))

第二 - 没有什么有趣的。因为没有人等到任务结束。所有调用都将在主线程exept中等待Client.SyncContext.InitializeAsync(store);