被异步等待线程卡住



在构造函数中,我想调用一种方法类型:

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));

最新更新