我对async/await
用法不熟悉。我试图在UI中有条件地抽象异步和await
。我有一个抽象的基类:
public abstract class Base
{
public abstract bool IsRunning { get; }
public abstract Task<bool> Run();
}
从中得到一些派生实例,第一个是同步的:
internal class Derived1 : Base
{
private readonly Base baseCase;
private Task<bool> task;
public Derived1(Base baseCase)
{
this.baseCase = baseCase;
}
public override bool IsRunning
{
get { return false; }
}
public override Task<bool> Run()
{
task = new Task<bool>(() =>
{
bool ok = DoSomething();
return ok;
});
return task;
}
}
和异步实现的派生类:
internal class Derived2 : Base
{
private readonly Base baseCase;
private Task<bool> task;
public Derived2(Base baseCase)
{
this.baseCase = baseCase;
}
public override bool IsRunning
{
get { return task != null && task.Status == TaskStatus.Running; }
}
public override Task<bool> Run()
{
task = new Task<bool>(() =>
{
bool ok = DoSomething();
return ok;
});
return task;
}
}
然后在UI中,我想在await
任务上asynchronous
(如果用户在运行时配置中指定),如下所示:
internal class CaseMenuHandler
{
private async void OnRun(object sender, EventArgs args)
{
foreach (var case in Cases)
{
Base baseCaseRunner = GetCaseRunner(case);
try
{
bool ok = true;
if( something_holds ) {
ok = await baseCaseRunner.Run();
}
else {
ok = baseCaseRunner.Run().Result;
}
}
catch (Exception e)
{
LogError(...);
}
}
}
希望这很清楚。我可以这样做,特别是在if块内有条件地等待吗?理想情况下,我希望Base
类只返回bool
而不是Task<bool>
方法,并且只有Run
类重写才能返回Derived2
,但我不清楚如何做到这一点。也许我应该在Task<bool>
的{{1}}方法中返回task.Result
?如果有更好的方法,包括抽象或任何其他更正,请告诉我。欣赏任何想法。
编辑#1
以下回复中阐明了Run
中同步实施的Derived2
方法表单。我不允许更改Run
方法的签名,因此,Derived1
中的DoSomething
方法(异步实现)现在看起来如下(感谢@ Stripling的评论) :
Run
编辑#2 :
当我尝试上述操作时(也尝试在Derived2
定义后调用 public override async Task<bool> Run()
{
task = new Task<bool>(() =>
{
bool ok = DoSomething();
return ok;
});
task.Start();
return await task;
}
,我收到以下错误:
task.Start()
答案 0 :(得分:5)
我可以执行上述操作,特别是在if块内有条件地等待吗?
你可以,但你不应该这样做。如果你做得对,那么以阻塞方式专门调用同步任务没有什么大的优势:作为一般规则,你可以只await
返回的任务,如果是代表一个同步任务,然后await
将以非常小的开销同步解决。
当我说'#34;如果你做得对,&#34;,这是正确的方法:
// synchronous
public override Task<bool> Run()
{
var result = DoSomething();
return Task.FromResult(result);
}
// asynchronous
public override async Task<bool> Run()
{
var result = await DoSomethingAsync();
return result;
}
await
上面第一个例子的结果不会做任何线程切换或类似的事情。根据{{1}}的实施情况await
第二个结果的结果DoSomethingAsync()
。对抽象层没有特别需要:您始终可以检查Task
是否已完成,await
已完成的任务将始终返回一个值。
答案 1 :(得分:2)
我看不到你的同步版本是如何同步的,你仍然使用Task.Run(
,我原本希望
internal class Derived1 : Base
{
private readonly Base baseCase;
private Task<bool> task;
public Derived1(Base baseCase)
{
this.baseCase = baseCase;
}
public override bool IsRunning
{
get { return false; }
}
public override Task<bool> Run()
{
bool ok = DoSomething();
return Task.FromResult(ok);
}
}
如果你这样做而不是你这样做,你的其他代码就变成了
private async void OnRun(object sender, EventArgs args)
{
foreach (var case in Cases)
{
Base baseCaseRunner = GetCaseRunner(case);
try
{
bool ok = true;
ok = await baseCaseRunner.Run();
}
catch (Exception e)
{
LogError(...);
}
}
}
异步版本将异步运行,同步版本将同步运行。
您的“异步版本”也不是真正的异步,请参阅Stripling's answer了解正确的方法。
答案 2 :(得分:2)
然后在UI中,我想等待异步任务(如果用户在运行时配置中指定)
首先,我不得不说这是一个非常糟糕的主意。有些东西应该是可配置的,有些则不应该。操作的异步不应该是可配置的。期。这是一个可怕的设计,我要做的第一件事是努力反击这样荒谬的“要求”。它实际上与是否抛出异常的可配置标志具有相同的意义。
那就是说,可以完成。在一天结束时,它很痛苦,难以维护,并且完全没用。但是,嘿,我认为你已经为此付出了代价,所以这一切都很好,嗯?
如果必须出于政治原因(绝对没有正当的技术原因),我建议您使用Boolean Argument Hack from my article on brownfield async。阻止(Result
)的一个问题是,如果Run
使用await
(reasons described on my blog),它就无效。
布尔参数hack只是添加一个布尔参数,指示该方法是否应该同步完成。
public abstract class Base
{
public abstract Task<bool> RunAsync(bool sync);
}
此处的语义是,如果sync
为true
,则RunAsync
返回的任务必须已完成。
您的实现如下:
internal class Derived1 : Base
{
public override async Task<bool> RunAsync(bool sync)
{
IsRunning = true;
try
{
if (sync)
return DoSomething();
return await Task.Run(() => DoSomething());
}
finally
{
IsRunning = false;
}
}
}
它被称为:
private async void OnRun(object sender, EventArgs args)
{
foreach (var case in Cases)
{
Base baseCaseRunner = GetCaseRunner(case);
try
{
bool sync = !something_holds;
bool ok = await baseCaseRunner.RunAsync(sync);
}
catch (Exception e)
{
LogError(...);
}
}
}
请注意,await
始终可以调用 ,但如果OnRun
为{sync
,true
实际上将同步 1}}。这是由于“等待快速路径” - 事实await
first checks whether the task is already complete, and if it is, it continues synchronously(如我的异步介绍博客文章中所述)。
答案 3 :(得分:0)
最简单的方法是遵循标准:提供同步和异步方式:
public abstract class Base
{
public abstract bool IsRunning { get; }
public abstract bool Run();
public abstract Task<bool> RunAsync();
}
public sealed class Implementer : Base
{
//... /*DoSomething returns a bool*/
public override bool Run() { return DoSomething().Result; } //SYNC
public override async Task<bool> RunAsync() { return await DoSomething(); } //ASYNC
//...
}
答案 4 :(得分:0)
使用Task.FromResult
返回包含同步计算结果的任务,然后等待。
bool ok = DoSomething();
return Task.FromResult(ok);
作为旁注,我不会将同步代码放在通常用于异步(或在其他类/位置同步)的方法中,反之亦然。我将为同步和异步实现使用不同的接口/基类。
interface IRunnable
{
bool IsRunning;
bool Run();
}
interface IRunnableAsync
{
bool IsRunning;
Task<bool> RunAsync();
}