c# - 异步,在 C# 中等待
问题描述
在关闭我的应用程序时,我必须做一些清理活动,我在 ClassA.cs 中写了类似的东西
protected override void OnExit(ExitEventArgs e)
{
Cleanup();
base.OnExit(e);
}
private async Task Cleanup()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (databaseInitialisation != null)
{
await databaseInitialisation.InitialiseDatabase();
}
databaseFile?.Dispose();
}
我还有一堂 ClassB.cs
public class DatabaseInitialisation : IDatabaseInitialisation
{
private readonly string filterOptimisationPath;
private readonly Task databaseInitialisation;
private IDbConnectionFactory ConnectionFactory { get; }
public async Task InitialiseDatabase() => await databaseInitialisation;
public DatabaseInitialisation(string databaseFilePath, string filterOptimisationPath)
{
this.filterOptimisationPath = filterOptimisationPath;
ConnectionFactory = new SqLiteConnectionFactory(databaseFilePath);
databaseInitialisation = Task.Run(() => CreateDatabase(databaseFilePath));
}
}
所以现在当我关闭我的应用程序时,有时当调用清理时,从等待开始的程序执行将转到 onexit 方法而不执行 dispose 方法
解决方案
问题的根源在于您需要async
从同步方法调用一个方法,而您无法创建该方法,async
因为它是 GUI 框架的一部分。这里的一般建议是让你的调用方法async
,但既然你不能,有几种方法可以做到这一点:
选项1 - Task.Run
更改您的OnExit
方法以Cleanup
在线程池线程上启动,然后同步等待使用GetAwaiter().GetResult()
以防止死锁。由于这仅在关闭时运行一次,应该没问题,但是如果这是一种经常运行的方法,则不建议启动这样的后台线程,因为它可能导致线程池饥饿。
protected override void OnExit(ExitEventArgs e)
{
Task.Run(async () => await Cleanup().ConfigureAwait(false))
.GetAwaiter().GetResult();
base.OnExit(e);
}
选项 2 -async void
和一个ManualResetEvent
这种方法避免.GetAwaiter().GetResult()
了在线程池线程上使用和启动,但是在清理中抛出的任何异常都会导致应用程序崩溃。
protected override void OnExit(ExitEventArgs e)
{
using var signal = new ManualResetEventSlim(false);
Cleanup(signal);
signal.Wait();
base.OnExit(e);
}
private async void Cleanup(ManualResetEventSlim signal)
{
if (databaseInitialisation != null)
{
await databaseInitialisation.InitialiseDatabase();
}
databaseFile?.Dispose();
signal.Set();
}
您还可以使用其中一种Wait
重载来包含超时。
附带说明一下,在这样的 GUI 应用程序中,您确实应该调用.ConfigureAWait(false)
您等待的每个不是直接从 GUI 事件调用的任务。不这样做会导致死锁。
这两种解决方案的一个警告是,如果您的 Cleanup 方法或其调用堆栈中的任何内容,这两种方法都会死锁,在这种情况下,需要另一种解决方案。
推荐阅读
- c++ - 类通过回调提供数据
- wix - 如何使用 wix 工具集跳过 msi 的 PIDKey 要求?
- php - 如何使用准备好的语句与 excel 导入数据 n Laravel
- python - 在数组中查找值
- java - javafxports 项目使用哪种属性文件编码?
- ionic-framework - 有没有办法从激光条码扫描仪而不是相机检测条码格式
- python - 使用带有索引的 np.where
- electron - 预加载脚本未在 electron@5.0.0 浏览器视图中加载
- ssh - 使用 ssh 在 Windows 上执行 Terraform 远程执行
- node.js - Nodejs Jest 单元测试无法从 Mongodb 内部返回值 find, findOne