If I have an application with a synchronous method, is it safe to call an async method as shown below on a UI thread or is there an issue or potential deadlock situation? I know that calling Wait will obviously cause issues, but I feel like this may work out alright.
public void MyMainMethod(){
var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
myLabel.Text = getResult;
}
I can successfully run on a UI thread without issue, but I feel as if I may be missing something. I understand that I could use a Task and ContinueWith, but in this example, I would want to wait for the result of the async method before exiting the synchronous method.
Update / Clarification
In the example above, let's assume that the MyMainMethod is an overridden method or a property, etc. and cannot be modified to be async.
async Main is now part of C# 7.2 and can be enabled in the projects advanced build settings. If you have a simple asynchronous method that doesn't need to synchronize back to its context, then you can use Task. WaitAndUnwrapException: var task = MyAsyncMethod();
Asynchronous methods should not be exposed purely for the purpose of offloading: such benefits can easily be achieved by the consumer of synchronous methods using functionality specifically geared towards working with synchronous methods asynchronously, e.g. Task.
Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.
The await operator doesn't block the thread that evaluates the async method. When the await operator suspends the enclosing async method, the control returns to the caller of the method.
Let's look at your code:
public void MyMainMethod(){
var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
myLabel.Text = getResult;
}
Regardless of what's taking place inside getResultAsync
, this code is blocking the UI thread when it calls task.Result
. In most cases, this is already wrong.
Further, the fact that your getResultAsync
is async
suggests there's already an async operation inside it. There is no reason to wrap it with Task.Run
, unless you perform a mix of CPU- and IO- bound tasks inside getResultAsync
. Even then, it may not be necessary (see this for more details).
You can control the await
continuation context inside getResultAsync
with ConfiureAwait(false)
, and should do so to avoid deadlocks and redundant context switches, where possible.
So, the code can be reduced to:
public void MyMainMethod(){
var getResult = getResultAsync().Result;
myLabel.Text = getResult;
}
As is, it still blocks the UI. To avoid blocking, you need to make it async
. See Async All the Way from Best Practices in Asynchronous Programming by Stephen Cleary.
If it cannot be modified to be async
(as clarified in the update to your question), then the above is the best you can get. Indeed, it still may cause a deadlock, depending on what's going on inside getResultAsync
, with out without Task.Run
. To avoid deadlocks, you should not attempt to access the UI thread with a synchronous call like control.Invoke
inside getResultAsync
, or await
any tasks scheduled on the UI thread with TaskScheduler.FromCurrentSynchronizationContext
.
However, usually it is possible and desirable to re-factor the code like this into an async version:
public async Task MyMainMethod(){
var getResult = await getResultAsync();
myLabel.Text = getResult;
}
You would be calling it from a top-level entry point of your app, like a UI event handler:
async void Button_Click(object sender, EventArg e)
{
try
{
await MyMainMethod();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
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