If a user performs an operation, such as deleting items, it removes them from the UI right away and then deletes them from the database on a background thread using TPL. The problem is if the user exits the application before the background thread finishes, the item never actually gets deleted.
Is there a standard way of waiting for async operations to finish before shutting down the application?
My async calls look like this:
if (MyObjectList.Contains(obj)) MyObjectList.Remove(obj);
Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));
Update
Here’s the final code I went with. I’m quite happy to see it works as it should, although let me know if I can improve it. I still have a lot to learn 🙂
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);
}
}
And when starting an important background task, I’m using this syntax:
var task = Task.Factory.StartNew(() => DAL<MyEntities>.DeleteObject(obj));
EventSystem.Publish<TaskStartedMessage>(new TaskStartedMessage(task));
await task;
EventSystem.Publish<TaskEndedMessage>(new TaskEndedMessage(task));
I’m using AsyncCTP for await/async, and Microsoft Prism’s EventAggregator for the event system.
There is no standard way but since you create a specific Task here it should be easy to put that in a List and build some Exit-logic to Wait for all Tasks in that List.
OK, a sample. Untested and incomplete:
The drawback is that you will have to extend each Task with the code to Add & Remove it.
Forgetting a Remove() (eg when an Exception happens) would be a (small) memory-leak. It is not too critical, instead of burdening your code with
using()blocks you could also periodically run a Cleanup() method that uses HashSet.RemoveWhere() to remove non-running tasks.