C# 对异步/等待函数的误解

C# 对异步/等待函数的误解,c#,.net,asynchronous,async-await,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=DoHeavyW

我使用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())
{
}
我尝试实现异步/等待

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;
}
这一次,没有错误,只是一个警告(我使用的是非英语版本的VS,所以我翻译了错误/警告消息):

此异步方法没有等待运算符,因此它将执行 作为同步的

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

但这不是大问题。调用父方法PopulateTV()时,我希望返回一个bool值
(如果(!PopulateTV())…
我得到一个错误:

!无法使用运算符 使用“System.Threading.Tasks.Task”

当然。但是
PopulateTV()
返回
bool
returnboolresult;
),即使方法声明使用
Task


我的错误在哪里?

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

尝试类似于
(等待任务。运行(()=>DoHeavyWork());

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

如果(!(wait PopulateTV()),则为
。首先等待结果(bool),然后对其进行评估

另一方面,您的异步方法并不等同于这样编码:

public Task<bool> DoHeavyWork()
{
  //do the work
  return Task.FromResult(true);
}
    public Task<bool> DoHeavyWork()
    {
        return Task.Factory.StartNew
        (
             () =>
             {
                  Thread.Sleep(10000);

                  return true;
             }
        )

    }
public async bool PopulateTV()
{
    using (var waitingForm = new WaitingForm())
    {
        waitingForm.Show();
        return await Task.Run(() => DoHeavyWork());
    }
}
…与这样写几乎是一样的:

public Task<bool> DoHeavyWork()
{
  //do the work
  return Task.FromResult(true);
}
    public Task<bool> DoHeavyWork()
    {
        return Task.Factory.StartNew
        (
             () =>
             {
                  Thread.Sleep(10000);

                  return true;
             }
        )

    }
public async bool PopulateTV()
{
    using (var waitingForm = new WaitingForm())
    {
        waitingForm.Show();
        return await Task.Run(() => DoHeavyWork());
    }
}
…这也可以使用
Task.FromResult(T result)
简化,因为您可以检入此答案的一些代码片段

那么,为什么我需要使用基于任务的异步模式(TAP)开发我的API呢? 因为方法的调用方不需要知道方法是同步的还是异步的。方法的实现将决定它,但同时,如果整个方法实际上是异步的,调用方可以异步工作

如果您执行相反的操作(同步方法),那么如果您决定将所有操作都实现为异步操作,则需要更改整个代码流:

// 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();
是正确的,但只需直接返回
任务
即可避免开销和脆弱性:

public Task<bool> DoHeavyWork()
{
    return Task.Run(() =>
    {
        return HeavyWorkReturningBool();
    });
}
DoHeavyWork
仍然是一种常规的同步方法:

public bool DoHeavyWork()
{
    var boolResult;
    //do the work
    return boolResult;
}
有关详细信息,请参阅以下博客帖子:

但是
PopulateTV()
返回bool(return boolResult;),即使 方法声明使用
任务

错误。
PopulateTV()
返回一个
任务

为什么在这样声明方法时会收到警告

public async Task<bool> DoHeavyWork()

将整个方法体包装在任务中的代码。Run对其行为撒谎;它为一个伪异步方法公开了一个异步API。从长远来看,将对任务中方法的调用包装在任务中。Run更易于维护。@StephenCleary,我不明白“伪异步”是什么意思。lambda中的所有内容不是都传递给
Task.Run()
以异步方式运行吗?不过我同意您关于可维护性的观点。其目的是包装单个调用,但我使用了中间
result
变量,以更明确地说明如何使用
Task
(即,在lambda内部,one只返回一个由
任务
的类型参数指示的类型值。我稍微更改了代码示例。“异步”可能意味着一些不同的事情。我将工作推送到后台线程称为“伪异步”,因为所有的代码都是通过使用另一个线程释放当前线程。相反,“真正的异步”代码不使用任何线程(一个示例是异步I/O)。在op的情况下,有只有同步的工作要做,所以使方法签名异步在我看来是误导性的。我在我的博客上有一个较长的描述,附加的改变是使
DoHeavyWork
return
bool
而不是
Task
。我认为这有点暗示:我喜欢你的网站。
if (!(await PopulateTV()))
public async Task<bool> DoHeavyWork()
public Task<bool> DoHeavyWork()
{
    return Task.Run<bool>(() => 
        //do the heavy work
        return boolResult;
    );
}