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.
Use async/await to Wait for a Function to Finish Before Continuing Execution. Another way to wait for a function to execute before continuing the execution in the asynchronous environment in JavaScript is to use async/wait .
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
However, just to address "Call an async method in C# without await", you can execute the async method inside a Task. Run . This approach will wait until MyAsyncMethod finish. await asynchronously unwraps the Result of your task, whereas just using Result would block until the task had completed.
The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.
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:
// 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);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With