简体   繁体   English

使用Task.FromResult(0)异步等待

[英]Async await with Task.FromResult(0)

Not sure if I am messed up with my understanding of how async await works, but here is the problem I am stucked at. 不知道我是否对异步等待的工作原理感到困惑,但这是我遇到的问题。 Consider a contrived example 考虑一个人为的例子

  1. This code blocks UI 该代码阻止了UI

     public async void LoginButtonClicked() { //create a continuation point so every following statement will get executed as ContinueWith await Task.FromResult(0); //this call takes time to execute Remote.Login("user","password"); } 
  2. But this does not (obviously) 但这并没有(显然)

      public void LoginButtonClicked() { Task.Run(()=>{ Remote.Login("user","password");}); } 

I like to use method 1 because I don't want to spin long work using a Task.Run rather I prefer framework handle this form me. 我喜欢使用方法1,因为我不想使用Task.Run花费大量时间,而我更喜欢由框架来处理这种形式。 But the problem is The call to Method 1 seems blocking. 但是问题是方法1的调用似乎阻塞了。

Using await/async only stops you from blocking the UI if all the long-running operations you call are async. 如果您调用的所有长时间运行的操作都是异步的,则使用await / async只能阻止您阻止UI。 In your example your Remote.Login is a synchronous call, so regardless of what the prior await line does, this will block your UI. 在您的示例中, Remote.Login是一个同步调用,因此,无论先前的await行做什么,这都会阻塞您的UI。

You need to either get an async version of your actual long-running operation (eg something returning a Task ) or if that is not possible, then you can resort to Task.Run in order to move this work to the ThreadPool . 您需要获得一个实际的长期运行操作的异步版本(例如,某个东西返回Task ),或者如果不可能,则可以诉诸Task.Run来将这项工作移至ThreadPool

What you want if possible: 如果可能,您想要什么:

public async void LoginButtonClicked()
{
    await Remote.LoginAsync("user","password");
    // do anything else required
}

Every async method has its context. 每个异步方法都有其上下文。

When the Task starts it might run in a new SynchronizationContext . 当Task启动时,它可能会在新的SynchronizationContext运行。 "Might" because if the task is already completed, like Task.FromResult(0) , then no other SynchronizationContext is created and the original one is used. “可能”,因为如果任务已经完成,例如Task.FromResult(0) ,则不会创建任何其他SynchronizationContext ,而是使用原始任务。

Awaiting for a task means that when the task is finished, the next statement will run in the original SynchronizationContext. 等待任务意味着完成任务后,下一条语句将在原始SynchronizationContext中运行。

This behavior can be changed using the Task.ConfigureAwait(continueOnCapturedContext: false) . 可以使用Task.ConfigureAwait(continueOnCapturedContext: false)更改此行为。 This means that the next statement will continue on the same context. 这意味着下一条语句将在相同的上下文中继续。 But this will change nothing by doing Task.FromResult(0).ConfigureAwait(false) because the task is already completed and the original context will be used. 但这不会通过执行Task.FromResult(0).ConfigureAwait(false)进行任何更改,因为该任务已经完成并且将使用原始上下文。

Therefore your Remote.Login("user","password"); 因此,您的Remote.Login("user","password"); will be run in the original context, thus blocking the UI thread which runs on the same context. 将在原始上下文中运行,从而阻止在相同上下文中运行的UI线程。

If you would have something like: 如果您有类似以下内容:

public async void LoginButtonClicked()
{
  await Task.Delay(5000).ConfigureAwait(false);
  Remote.Login("user","password");
}

Then Remote.Login("user","password"); 然后Remote.Login("user","password"); would execute in the thread pool context thus being on a different context than the original UI context. 将在线程池上下文中执行,因此与原始UI上下文位于不同的上下文中。

So the best way to fix your code is to create a Remote.LoginAsync() as stated in @Nicholas W answer. 因此,修复代码的最佳方法是按照@Nicholas W答案中的说明创建Remote.LoginAsync()

NOTE on performance: if you have an async method with multiple await statements, and you don't need some of those awaits to do work on UI or web app thread, then you can use Task.ConfigureAwait(false) in order to prevent multiple switches to the UI/web-app context which slices its execution time. 关于性能的注意事项:如果您有一个带有多个await语句的async方法,并且不需要其中的某些await来在UI或Web应用程序线程上工作,那么可以使用Task.ConfigureAwait(false)来防止出现多个切换到UI / web-app上下文,该上下文可分割其执行时间。

  1. You run in parallel Task.FromResult(0); 您在并行Task.FromResult(0);中运行; and still wait for Remote.Login("user","password"); 仍在等待Remote.Login(“ user”,“ password”); to be finished 完成
  2. You run Remote.Login("user","password"); 您运行Remote.Login(“ user”,“ password”); asynchronously. 异步。

You have to create async version of Remote.Login 您必须创建Remote.Login的异步版本

    async Task LoginAsync(string user, string password)
    {
        Remote.Login(user, password);
        await Task.FromResult(0);
    }

and call it 叫它

    public async void LoginButtonClicked()
    {
        await LoginAsync("user", "password");
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM