Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/311.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当应用程序使用退出时,如何等待异步操作完成?_C#_Wpf_Asynchronous_Task Parallel Library - Fatal编程技术网

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

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

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

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

我的异步调用如下所示:

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