我使用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()
会返回bool
(return boolResult;
),即使方法声明使用Task<bool>
。
我的错误在哪里?
答案 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)
进行简化,因为您可以查看此答案的一些代码段。
因为方法的调用者不需要知道方法是同步还是异步。方法的实现将决定它,但同时如果整个方法实际上是异步的,则调用者可以异步工作。
如果您执行相反的操作( 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;
);
}