当应用程序退出时,我如何等待我的异步操作完成

本文关键字:等待 我的 异步操作 何等待 退出 应用程序 | 更新日期: 2023-09-27 18:18:51

如果用户执行操作(例如删除项目(,它会立即从 UI 中删除它们,然后使用 TPL 从后台线程上的数据库中删除它们。问题是,如果用户在后台线程完成之前退出应用程序,则该项目实际上永远不会被删除。

是否有在关闭应用程序之前等待异步操作完成的标准方法?

我的异步调用如下所示:

if (MyObjectList.Contains(obj)) MyObjectList.Remove(obj);
Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));

更新

这是我使用的最终代码。我很高兴看到它正常工作,尽管让我知道我是否可以改进它。我还有很多东西要学:)

public partial class App : Application
{
    private List<Task> _backgroundTasks = new List<Task>();
    public App()
    {
        EventSystem.Subscribe<TaskStartedMessage>((e) =>
        {
            _backgroundTasks.Add(e.Task);
        });
        EventSystem.Subscribe<TaskEndedMessage>((e) =>
        {
            if (_backgroundTasks.Contains(e.Task))
                _backgroundTasks.Remove(e.Task);
        });
    }
    protected override void OnExit(ExitEventArgs e)
    {
        Task.WaitAll(_backgroundTasks.Where(p => !p.IsCompleted).ToArray(), 30000);
        base.OnExit(e);
    }
}

在启动重要的后台任务时,我使用以下语法:

var task = Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));
EventSystem.Publish<TaskStartedMessage>(new TaskStartedMessage(task));
await task;
EventSystem.Publish<TaskEndedMessage>(new TaskEndedMessage(task));

我正在使用AsyncCTP进行await/async,并为事件系统Microsoft Prism的EventAggregator

当应用程序退出时,我如何等待我的异步操作完成

没有标准的方法,但由于您在此处创建了一个特定的任务,因此应该很容易将其放入列表中并构建一些退出逻辑来等待该列表中的所有任务。

好的,一个示例。未经测试和不完整:

// untested
static class CriticalTasks
{
    static HashSet<Task> tasks = new HashSet<Task>();
    static object locker = new object();
    // when starting a Task
    public static void Add(Task t)
    {
        lock(locker)
           tasks.Add(t);
    }
    // When a Tasks completes
    public static void Remove(Task t)
    {
        lock(locker)
           tasks.Remove(t);
    }
    // Having to call Remove() is not so convenient, this is a blunt solution. 
    // call it regularly
    public static void Cleanup()
    {
        lock(locker)
           tasks.RemoveWhere(t => t.Status != TaskStatus.Running);
    }
    // from Application.Exit() or similar. 
    public static void WaitOnExit()
    {
        // filter, I'm not sure if Wait() on a canceled|completed Task would be OK
        var waitfor = tasks.Where(t => t.Status == TaskStatus.Running).ToArray();
        Task.WaitAll(waitfor, 5000);
    }
}


缺点是您必须使用代码扩展每个任务以添加和删除它。

忘记 Remove(((例如,当异常发生时(将是一个(小(内存泄漏。这不是太关键,除了用using()块负担你的代码,你也可以定期运行一个 Cleanup(( 方法,该方法使用 HashSet.RemoveWhere(( 来删除非运行的任务。

var x = Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));

表单关闭事件:

while(!x.IsCompleted){hide form}

if(!x.IsCompleted)
   //cancel exit

您可以存储对任务的引用,并在应用程序退出时等待它(例如,在退出事件上(。

您也可以创建一个普通线程,确保将 IsBackground 设置为 false(默认值(。在所有非后台线程完成其工作之前,进程不会退出,因此它将运行到最后,您必须注意不要使用任何 GUI 逻辑或确保不会从此线程中释放所需的对象。

代替一个蹩脚的代码示例。答案是你根本做不到。 如果应用程序域开始终止,则在强制终止所有此类任务之前,您有一个有限的窗口来执行清理任务。 您最好的做法是使用某种作业控件,以便有状态地将工作排队。 这也将防止异常终止。

否则,在执行 WaitAll 并完全完成之前,您不会开始退出。