C# 当应用程序使用退出时,如何等待异步操作完成?
如果用户执行某个操作,例如删除项目,它会立即从UI中删除它们,然后使用TPL从后台线程上的数据库中删除它们。问题是,如果用户在后台线程完成之前退出应用程序,则该项实际上永远不会被删除 在关闭应用程序之前,是否有一种等待异步操作完成的标准方法 我的异步调用如下所示:C# 当应用程序使用退出时,如何等待异步操作完成?,c#,wpf,asynchronous,task-parallel-library,C#,Wpf,Asynchronous,Task Parallel Library,如果用户执行某个操作,例如删除项目,它会立即从UI中删除它们,然后使用TPL从后台线程上的数据库中删除它们。问题是,如果用户在后台线程完成之前退出应用程序,则该项实际上永远不会被删除 在关闭应用程序之前,是否有一种等待异步操作完成的标准方法 我的异步调用如下所示: if (MyObjectList.Contains(obj)) MyObjectList.Remove(obj); Task.Factory.StartNew(() => DAL<MyEntities>.Delete
if (MyObjectList.Contains(obj)) MyObjectList.Remove(obj);
Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));
if(MyObjectList.Contains(obj))MyObjectList.Remove(obj);
Task.Factory.StartNew(()=>DAL.DeleteObject(obj));
更新
这是我最后的代码。我很高兴看到它的工作,因为它应该,虽然让我知道,如果我可以改善它。我还有很多东西要学:)
公共部分类应用程序:应用程序
{
私有列表_backgroundTasks=新列表();
公共应用程序()
{
EventSystem.Subscribe((e)=>
{
_背景任务。添加(如任务);
});
EventSystem.Subscribe((e)=>
{
如果(_backgroundTasks.Contains(e.Task))
_背景任务。删除(如任务);
});
}
受保护的覆盖void OnExit(ExitEventArgs e)
{
Task.WaitAll(_backgroundTasks.Where(p=>!p.IsCompleted).ToArray(),30000);
基数: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));
var task=task.Factory.StartNew(()=>DAL.DeleteObject(obj));
发布(新任务启动消息(任务));
等待任务;
发布(新TaskEndedMessage(task));
我将AsyncCTP用于
Wait
/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);
}
}
//未经测试
静态类关键任务
{
静态HashSet tasks=新HashSet();
静态对象锁定器=新对象();
//开始一项任务时
公共静态无效添加(任务t)
{
锁(储物柜)
任务。添加(t);
}
//当任务完成时
公共静态无效删除(任务t)
{
锁(储物柜)
任务。删除(t);
}
//必须调用Remove()不是那么方便,这是一个直截了当的解决方案。
//定期打电话
公共静态无效清除()
{
锁(储物柜)
tasks.removehere(t=>t.Status!=TaskStatus.Running);
}
//从Application.Exit()或类似文件。
公共静态void WaitOnExit()
{
//过滤器,我不确定对已取消的|已完成任务的等待()是否可以
var waitfor=tasks.Where(t=>t.Status==TaskStatus.Running.ToArray();
Task.WaitAll(waitfor,5000);
}
}
缺点是您必须使用代码扩展每个任务以添加和删除它
忘记Remove()(如发生异常时)将导致(小的)内存泄漏。这并不太重要,您也可以定期运行Cleanup()方法,该方法使用HashSet.RemoveWhere()删除未运行的任务,而不是用
using()
块加重代码负担 您可以存储对任务的引用,并在应用程序退出时等待它(例如在退出事件时)
您也可以创建一个普通线程,确保设置为false
(默认值)。进程将不会退出,直到所有非后台线程完成它们的工作,所以它将一直运行到最后,您必须注意不要使用任何GUI逻辑或确保您不会处理此线程中需要的对象
var x = Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));
或
而不是一个蹩脚的代码示例。答案是你根本不能。如果应用程序域开始终止,则在强制终止所有此类任务之前,您有一个执行清理任务的有限窗口。最好的做法是使用某种作业控制,将工作有状态地排入队列。这也将防止异常终止
否则,在执行WaitAll并完全完成之前,您不会开始退出。在后台线程上运行它的全部目的是防止它在删除对象时锁定UI。这不就是在主线程上运行删除代码并锁定UI吗?@Rachel:Thead being background与是否是UI的后台无关。后台线程是当所有非后台线程退出时应该终止的线程。非后台线程将继续它的工作。因此,如果您在非后台线程上运行数据库逻辑,它将独立于您的主UI完成其工作。谢谢:)我喜欢看到代码示例,因为它为我指明了正确的方向,而且通常我可以从中学习新的东西。@Rachel,好的,但我现在意识到您需要使其线程安全。稍后我将进行编辑。最终的代码看起来不错,但是我会删除foreach并将其替换为:Task.WaitAll(_backgroundTasks.ToArray());我想不出你的实现会带来什么麻烦,但我猜如果他们实现了那个静态方法,他们是有道理的。@Baboon谢谢,我不知道有一个
Task.WaitAll()
我可以在任务列表上使用的方法我确实建议避免这种将核心提升到100%的旋转,您可以添加一些关于您答案的描述吗?是的。我将添加评论。道歉。简而言之,如果你希望完成所有任务,就不能开始退出该计划。因此,将Tasks.WaitAll放在OnExit处理程序中是一个糟糕的选择,因为一旦应用程序域决定关闭它,您就有一个很小的时间窗口来执行清理,一旦该时间段结束,应用程序将强制终止。因此,基本上我的代码只是按照
var x = Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));
while(!x.IsCompleted){hide form}
if(!x.IsCompleted)
//cancel exit