误解async / await函数

时间:2015-06-12 14:03:33

标签: c# .net asynchronous async-await

我使用C#/ VS2012 / .Net4.0 / Microsoft.Bcl.async NuGet包

在之前的问题之后,我尝试使用async / await来避免冻结UI。但是我的每一项测试都没有成功。

执行繁重工作的主要功能是这样的:

public bool PopulateTV()
{
  using (WaitingForm waitingForm=new WaitingForm())
  {
    waitingForm.Show();
    bool boolResult=DoHeavyWork(); //UI is freezing, waitingForm() doesn't show correctly
    return boolResult;
  }
}

对此功能的调用类似于

if(!PopulateTV())
{
}

我尝试实现async / await

public async Task<bool> populateTV()
{
  using (WaitingForm waitingForm=new WaitingForm())
  {
    Task<bool> func=DoHeavyWork();
    bool boolResult=await func;
    return boolResult;
  }
}

public Task<bool> DoHeavyWork()
{
  //do the work
  return boolResult;
}

当然,我在DoHeavyWork收到错误,因为我返回bool而不是Task<bool>。所以我改变了DoHeavyWork函数:

public async Task<bool> DoHeavyWork()
{
  //do the work
  return boolResult;
}

这一次,没有错误,但是警告(我使用非英文版的VS,所以我翻译了错误/警告信息):

  

此异步方法没有await运算符,因此它将执行   同步。

这只是一个警告,但为什么我会收到此消息?

但这不是大问题。当我调用父方法PopulateTV()时,我想返回一个bool值(if(!PopulateTV()) ... 我收到一个错误:

  

!运算符不能使用   使用'System.Threading.Tasks.Task'。

当然。但PopulateTV()会返回boolreturn boolResult;),即使方法声明使用Task<bool>

我的错误在哪里?

4 个答案:

答案 0 :(得分:6)

你实际上并没有在一个单独的线程中运行繁重的代码 - 异步并不能自动保证这一点。

尝试(await Task.Run(() => DoHeavyWork()));

之类的内容

答案 1 :(得分:4)

fk2's answer是正确的,但只需直接返回Task<bool>就可以避免开销和脆弱:

public Task<bool> DoHeavyWork()
{
    return Task.Run(() =>
    {
        return HeavyWorkReturningBool();
    });
}

有关详细信息,请参阅this question

然而,正如评论中所指出的,这不是一个干净的解决方案。将工作卸载到后台线程以保持UI响应性属于UI代码,而不是实现工作的API。对于更模块化的解决方案,您应该以这种方式实现它:

public async bool PopulateTV()
{
    using (var waitingForm = new WaitingForm())
    {
        waitingForm.Show();
        return await Task.Run(() => DoHeavyWork());
    }
}

DoHeavyWork仍然是常规的同步方法:

public bool DoHeavyWork()
{
    var boolResult;
    //do the work
    return boolResult;
}

有关此内容的详情,请参阅以下博文:

答案 2 :(得分:1)

  

但这不是大问题。当我调用父方法时   PopulateTV(),我想返回一个bool值(if(!PopulateTV())...并且   我收到一个错误:

这是if(!(await PopulateTV()))。首先等待结果(bool)然后评估它。

另一方面,你不是那种异步方法就等同于这样的代码:

public Task<bool> DoHeavyWork()
{
  //do the work
  return Task.FromResult(true);
}

async/await个关键字可用作语法糖,以便将您的task-based asynchronous pattern代码重写为更易读的代码。

实际上,如果你的DoHeavyWork做了类似的事情:

public async Task<bool> DoHeavyWork()
{
  await Task.Factory.StartNew(() => Thread.Sleep(10000));

  return true;
}

...几乎与以这种方式书写相同:

    public Task<bool> DoHeavyWork()
    {
        return Task.Factory.StartNew
        (
             () =>
             {
                  Thread.Sleep(10000);

                  return true;
             }
        )

    }

您需要了解async/await只是一个编译器功能,可以让您编写优雅的异步代码,但这并不意味着异步方法必须是异步的。这意味着可以使用Task关键字等待await个实例。

也就是说,您可以使用异步签名(即Task MyMethod() { })实现方法,但假定它们返回由Task生成的TaskCompletionSource<T>

public Task<bool> DoHeavyWork()
{
  // No async operation here. This is just a fake!
  TaskCompletionSource<bool> completion = new TaskCompletionSource<bool>();
  completion.SetResult(true);

  return completion.Task;
}

...这也可以使用Task.FromResult<T>(T result)进行简化,因为您可以查看此答案的一些代码段。

那么,为什么我需要使用基于任务的异步模式(TAP)开发我的API?

因为方法的调用者不需要知道方法是同步还是异步。方法的实现将决定它,但同时如果整个方法实际上是异步的,则调用者可以异步工作

如果您执行相反的操作( sync methods ),如果您决定将所有内容实现为异步操作,则需要更改整个代码流:

// I don't know if it's actually async or not, I don't care. The calling method
// decides it.
bool result = await DoWorkAsync();

// I know it's sync. So my code can become pure garbage if I need to make it
// synchronous since it can't be awaited and it doesn't return a Task instance!
bool result = DoWork();

答案 3 :(得分:0)

  

但是PopulateTV()返回一个bool(返回boolResult;),即使是   方法声明使用Task<bool>

错误。 PopulateTV()会返回Task<bool>

//        (return Type is here)
//           ||||||||||
//           vvvvvvvvvv
public async Task<bool> populateTV()

只要将return boolResult;视为Task的return语句,而不是方法。要获得实际的bool结果,您需要等待任务:

if (!(await PopulateTV()))

为什么在宣布这样的方法时会收到警告?

public async Task<bool> DoHeavyWork()

因为您使用async而没有await。您在此处不需要async关键字。所有async都会启用await,因此如果您不使用await,请不要使用async。您只需返回任务:

public Task<bool> DoHeavyWork()
{
    return Task.Run<bool>(() => 
        //do the heavy work
        return boolResult;
    );
}