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

Rac*_*hel 12 c# wpf asynchronous task-parallel-library

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

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

我的异步调用看起来像这样:

if (MyObjectList.Contains(obj)) MyObjectList.Remove(obj);
Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));
Run Code Online (Sandbox Code Playgroud)

更新

这是我最后的代码.我很高兴看到它能够正常工作,但是如果我能改进的话,请告诉我.我还有很多东西需要学习:)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在开始一项重要的后台任务时,我正在使用以下语法:

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

我正在使用AsyncCTP for await/ async,而Microsoft Prism EventAggregator用于事件系统.

Hen*_*man 10

没有标准方法,但由于您在此处创建特定任务,因此应该很容易将其放入List中并构建一些Exit-logic以等待该List中的所有任务.

好的,样品.未经测试和不完整:

// 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);
    }
}
Run Code Online (Sandbox Code Playgroud)


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

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