在构造函数中,我想调用一种方法类型:
private async Task OnLoadPrometDanKorisnikDatum
我想等这个方法完成,我有更多这样的方法(3(,我想在后台线程中调用这3个方法,不要等他完成,只想等第一个方法。我想让他们并行执行。我有async Task方法,在视图模型的构造函数中,我像这样调用
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat,
DatumVrednost).Wait();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).Wait();
若我不把.Wet((放在末尾,程序就不能工作。我看到它们在调试模式下异步运行,但所花的时间告诉我它们是sub(一个方法时间+第二个方法时间+.(。有人能帮我吗,这是给我的,非常愚蠢。。。
答案
处理场景的最佳方法是使用async void
。
我建议先阅读下面的Explanation
部分,以充分了解async void
的最佳实践。
public MyConstructor()
{
ExecuteAsyncMethods();
}
async void ExecuteAsyncMethods()
{
try
{
await OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost);
await OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja);
}
catch(Exception e)
{
//Handle Exception
}
}
解释
许多C#开发人员被教导"永远不要使用async void
",但这是为数不多的使用案例之一
是的,async void
可能很危险,原因如下:
- 不能用
async avoid
方法await
- 可能导致比赛条件
- 很难捕获
async void
方法抛出的Exception
- 例如,以下
try/catch
块将不会捕获此处抛出的Exception
:
- 例如,以下
public MyConstructor()
{
try
{
//Cannot await `async void`
AsyncVoidMethodWithException();
}
catch(Exception e)
{
//Will never catch the `Exception` thrown in `AsyncVoidMethodWithException` because `AsyncVoidMethodWithException` cannot be awaited
}
//code here will be executing by the time `AsyncVoidMethodWithException` throws the exception
}
async void AsyncVoidMethodWithException()
{
await Task.Delay(2000);
throw new Exception();
}
也就是说,只要我们将整个async void
的内容封装在try/catch
块中,我们就能够捕获异常,如下所示:
public MyConstructor()
{
AsyncVoidMethodWithException();
}
async void AsyncVoidMethodWithException()
{
try
{
await Task.Delay(2000);
throw new Exception();
}
catch(Exception e)
{
//Exception will be caught and successfully handled
}
}
SafeFire和Forget
我创建了一个库来帮助实现这一点,它的额外好处是避免了编写可能被未来开发人员滥用的async void
代码。
它是开源的,也可以在NuGet:上使用
- 源代码
- NuGet包
SafeFireAndForget
SafeFireAndForget
允许我们安全地执行Task
,同时不阻塞调用线程,也不需要等待它完成后再转到下一行代码。
以下是SafeFireAndForget
的简化版本,您可以将其添加到项目中。
然而,我建议复制/粘贴其完整的源代码,或将其NuGet包添加到库中,以获得更健壮的实现
public static async void SafeFireAndForget<TException>(this Task task, Action<TException> onException = null, bool continueOnCapturedContext = false) where TException : Exception
{
try
{
await task.ConfigureAwait(continueOnCapturedContext);
}
catch (TException ex) when (onException != null)
{
onException(ex);
}
}
使用SafeFireAndForget
要使用SafeFireAndForget
,请将其附加到方法调用中,如下所示:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget();
要处理该Task
引发的任何Exception
,请使用onException
。下面是一个将Exception
打印到调试控制台的示例:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget(ex => Debug.WriteLine(ex));
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget(ex => Debug.WriteLine(ex));