wondra
wondra

Reputation: 3573

Async action hangs/deadlocks, why? (not waiting for Result, nor ConfiguringAwait)

First, sorry for yet another "why my async action hangs" question but I believe this question is different enough.
Surveying dozens of similar questions, the problem of async action deadlock is either in locking yourself out (.Result), using limited resources or using library components incorrectly (web requests seems popular). In the following example, I cannot find any from above:

private async Task ExecuteAsync(Task<int> task)
{
    // entering on current thread, that is the main UI thread
    await task // execute "task" asynchronnously (on a different thread)
        .ConfigureAwait(false); // when done, no need to return to main thread
    MessageBox.Show("success"); // succes indicator
}

public MainWindow() //wpf window ctor
{            
    InitializeComponent();
    this.Loaded += MainWindow_Loaded;
}

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    var task = new Task<int>(() => 42); // make an Action wrapping sychronnous method
    // fire and forget, never caring for .Result, disard even the task
    var _ = ExecuteAsync(task).ConfigureAwait(false); 
}

I have commented the sample with my best try on exaplaining how things (should) work, but something in my explanation must be wrong. Even though MainWindow ctor does not deadlock, the Action () => 42 is never executed and "success" message is not shown. After some debugging I managed to fix the sample (using Task.FromResult), but I am still not sure what is wrong with it as it is now and even more importantly why.

What is the error in my reasoning and why was the action never executed/finished?

Upvotes: 0

Views: 100

Answers (1)

Ren&#233; Vogt
Ren&#233; Vogt

Reputation: 43876

You did not start the task! You only declared it. Simply awaiting it does not "fire" it.

private async Task ExecuteAsync(Task<int> task)
{
    // at first do start the task
    task.Start();

    await task.ConfigureAwait(false); 
    MessageBox.Show("success");
}

Note that ConfigureAwait(false) does not guarantee that execution will be continued on a different thread. It only says that you don't need it to be resumed on the original thread. And resuming UI work (like MessageBox.Show()) on a non-UI thread is not recommended.


As NineBerry pointed out, if you want to wrap a synchronous method and let it run on a different thread, you should use Task.Run():

var task = Task.Run(() => YourSynchronousCall());

Upvotes: 5

Related Questions