Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I wait for my async operations to finish when the application gets exited using?

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.

like image 372
Rachel Avatar asked Dec 23 '11 14:12

Rachel


People also ask

How do you wait for async method to finish?

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 .

What happens when async method is not awaited?

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.

How do you call async method without await?

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.

What is async await method?

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.


1 Answers

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.

like image 112
Henk Holterman Avatar answered Sep 23 '22 03:09

Henk Holterman