使用 Xamarin Android 版本 8、.net 标准运行或不运行包含服务和活动的 ConfigureAwait(false)

Pin*_*ong 5 c# task-parallel-library xamarin.android async-await xamarin

我遇到了以下有关何时何地使用的文章ConfigureAwait(false),但无法得到答案。

您不需要 ConfigureAwait(false),但仍可在库和 UI 应用程序中使用它。(例如 Xamarin、WinForms 等)

https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html

此链接说相反的答案

为所有服务器端代码调用 ConfigureAwait 的最佳实践

正确使用 Task.Run 和 async-await 时

我的问题:

场景 1:下面的代码作为后台服务运行。

我的问题:ConfigureAwait(false)无论何时await都需要像下面的 A 和 B 一样使用:

    [Service(Name = "com.MainApplicationService", Label = "Main Application Service", Exported = false)]
    public class MainApplicationService : Android.App.Service
    {

        public override IBinder OnBind(Intent intent)
        {
            return null;
        }

        [return: GeneratedEnum]
        public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
        {
            await InitAsync().ConfigureAwait(false);   //line A

            Task.Run(async () => await InitAsync().ConfigureAwait(false));   //line B

            return StartCommandResult.Sticky;
        }
    }
Run Code Online (Sandbox Code Playgroud)

场景 2:下面的代码作为 UI 线程而不是后台服务运行

同样的问题:ConfigureAwait(false)每当使用 await 时都需要,就像下面的 C 和 D 一样:

public class StartupActivity : Android.App.Activity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        await InitAsync().ConfigureAwait(false);  //line C

        Task.Run(async () => await InitAsync().ConfigureAwait(false));  //line D

        Finish();
    }
}
Run Code Online (Sandbox Code Playgroud)

Xamarin Android 版本 8,我认为它是 .net 标准。

https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md

nos*_*tio 3

也许这是一个不受欢迎的观点,但现在我ConfigureAwait(false)什至在图书馆也不使用,请参阅:

重访Task.ConfigureAwait(continueOnCapturedContext: false)

IMO,如果使用基于 - 的 API 的代码Task关心当前同步上下文以及它可能如何影响该 API 的行为(死锁、冗余上下文切换等),它可以显式地包装 API 调用Task.Run或使用类似TaskExt.WithNoContextfrom的内容上面的链接:

await Task.Run(() => InitAsync());
// or
await TaskExt.WithNoContext(() => InitAsync());
Run Code Online (Sandbox Code Playgroud)

但在大多数情况下,特别是对于 UI 应用程序(有同步上下文,但线程可扩展性不是问题),可以保持原样,不带Task.Runor ConfigureAwait

await InitAsync();
Run Code Online (Sandbox Code Playgroud)

这将使您有机会发现并调查潜在的死锁,然后再尝试使用ConfigureAwait(false)或缓解它们Task.Run

因此,继续使用相同的同步上下文并不总是async void一个坏主意,特别是在将未处理的异常发布到当前同步上下文的方法内部,请参阅TAP 全局异常处理程序


更新回答评论中的问题:

await Task.Run(() => InitAsync());Task.Run(async () => await InitAsync());和有什么区别await Task.Run(async () => await InitAsync());

在这种情况下(一个简单的asynclambda to Task.Run),差异只是 async/await 编译器生成的状态机的额外开销,您不需要。无论哪种方式,由 返回的任务InitAsync都将自动解开。Task.Run对于更一般的情况,请参阅“await Task.Run();之间的任何区别” return;”和“返回Task.Run()”? ”。

async仅当我需要在完成后执行其他操作时,我才会在此处使用lambda InitAsync,同时仍然不必担心同步上下文,例如:

await Task.Run(async() => {
    await InitAsync();
    // we're on a pool thread without SynchronizationContext
    log("initialized");
});
Run Code Online (Sandbox Code Playgroud)

双重检查:使用像这样的丢弃_ = WorkAsync();来抑制警告,但它不会捕获异常。为了处理异常,我需要定义一个扩展方法,例如Forget. 关于 即发即忘方法

是的,这就是我的“一劳永逸”的选择。然而,我不认为你InitAsync的情况是真正的“一劳永逸”。也许,最好在类实例中跟踪它:_task = InitAsync()_task稍后观察。

或者,更好的是,您可以async void在内部使用辅助方法OnCreate来观察以下结果/异常InvokeAsync

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);

    async void function invokeInitAsync()
    {
        try 
        {
            await InitAsync();
            Finish();
        }
        catch(Exception e) {
            // handle the failure to initialize
            await promptAndExitUponErrorAsync(e);
        }
    }

    invokeInitAsync();
}
Run Code Online (Sandbox Code Playgroud)

可能可以创建OnCreateself async void,但是异常(如果有)base.OnCreate()不会同步传播到覆盖的调用者,这可能会产生其他副作用。因此,我将使用一个辅助async void方法,它也可以是本地的,如上所述。

最后,考虑在您的层中拥抱异步ViewModel,然后您就不必在像OnCreate. 有关更多详细信息,请参阅:“如何在 WPF 中使用异步初始化对 ViewModel 进行单元测试”。


归档时间:

查看次数:

736 次

最近记录:

6 年,1 月 前