Async等待Task.FromResult(0)

时间:2014-01-10 07:30:54

标签: c# async-await

不确定我是否对我对isync await如何工作的理解感到困惑,但这是我坚持的问题。 考虑一个人为的例子

  1. 此代码阻止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. 但这不(显然)

     public void LoginButtonClicked()
     {
     Task.Run(()=>{ Remote.Login("user","password");});
      }
    
  3. 我喜欢使用方法1,因为我不想使用Task.Run来旋转长时间工作,而是我更喜欢框架处理这个形式。但问题是对方法1的调用似乎是阻塞的。

3 个答案:

答案 0 :(得分:4)

如果您调用的所有长时间运行的操作都是异步的,则使用await / async只会阻止您阻止UI。在您的示例中,Remote.Login是同步调用,因此无论之前的await行是什么,都会阻止您的UI。

您需要获得实际长时间运行操作的异步版本(例如返回Task的内容)或者如果不可能,那么您可以求助Task.Run以便移动这项工作是ThreadPool

如果可能,您想要什么:

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

答案 1 :(得分:1)

每个异步方法都有其上下文。

当任务启动时,它可能会以新的SynchronizationContext运行。 “可能”因为如果任务已经完成,例如Task.FromResult(0),那么就不会创建其他SynchronizationContext并使用原始的Task.ConfigureAwait(continueOnCapturedContext: false)

等待任务意味着当任务完成时,下一个语句将在原始的SynchronizationContext中运行。

可以使用Task.FromResult(0).ConfigureAwait(false)更改此行为。这意味着下一个语句将在相同的上下文中继续。但是,通过执行Remote.Login("user","password");,这将不会改变任何内容,因为任务已经完成,并且将使用原始上下文。

因此,您的public async void LoginButtonClicked() { await Task.Delay(5000).ConfigureAwait(false); Remote.Login("user","password"); } 将在原始上下文中运行,从而阻止在相同上下文中运行的UI线程。

如果您有类似的话:

Remote.Login("user","password");

然后Remote.LoginAsync()将在线程池上下文中执行,因此与原始UI上下文不同。

因此修复代码的最佳方法是创建{@ 1}},如@Nicholas W答案中所述。

关于性能的说明:如果您有一个带有多个await语句的异步方法,并且您不需要其中一些等待在UI或Web应用程序线程上工作,那么您可以使用{ {1}}以防止多次切换到切片执行时间的UI / web-app上下文。

答案 2 :(得分:-2)

  1. 并行运行Task.FromResult(0);并仍在等待Remote.Login(“用户”,“密码”);待完成
  2. 您运行Remote.Login(“user”,“password”);异步。
  3. 您必须创建Remote.Login的异步版本

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

    并将其命名为

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