The mistake, as pointed by Matthew Watson in a comment, is that the Dispatcher.Invoke()
will run the code on the UI thread, so the Thread.Sleep
will be blocking the UI thread.
Now let's see how this problem can be fixed. I am assuming that your code is an event handler, and you want this handler to run asynchronously. This is the only valid scenario for using an async void
method. In any other case you should use async Task
.
private async void Button1_Click(object sender, RoutedEventArgs e)
{
if (irTryCounter % 3 == 0)
{
grdLogin.IsEnabled = false;
await Task.Run(() =>
{
// Here you can run any code that takes a long time to complete
// Any interaction with UI controls is forbidden here
// This code will run on the thread-pool, not on the UI thread
Thread.Sleep(10000);
});
grdLogin.IsEnabled = true;
}
}
In case the long-running operation returns a result that needs to be passed to the UI thread, use the Task.Run
overload that returns a Task<TResult>
:
int result = await Task.Run(() =>
{
Thread.Sleep(10000);
return 13;
});
// We are back on the UI thread, and the result is available for presentation
I am assuming that the Thread.Sleep(10000)
is a placeholder for an actual synchronous operation that takes a long time to complete. It would be even better if this operation can be performed asynchronously, because it would not require consuming a ThreadPool
thread for the whole duration of the operation. As an example, a delay can be performed asynchronously by using the Task.Delay
method:
await Task.Run(async () =>
{
await Task.Delay(10000);
});
In this case the Task.Run
wrapper may seem redundant, but actually it's not. Wrapping your asynchronous code in a Task.Run
protects your application from badly behaving asynchronous APIs that block the current thread. The overhead of including the wrapper is minuscule, and should have no noticeable effect in a WinForms/WPF application. The exception is ASP.NET applications, where Task.Run
ing is inadvisable.