Reputation: 17
I'm trying to call an async task (SIn) within a synch method (SignIn). I need the synch method because I'm passing ref to that method. But when I'm calling the async task, the GUI is frozen. The async task is a simple login with the onedrive sdk.
I've tried to waited the task, but the GUI still frozen. I've also tried creating a new Thread, but it didn't work too. How can I call the async method?
public override bool SignIn(ref User user)
{
try
{
signInEnd = false;
signinUser = user;
Task<bool> task = SIn();
task.Wait();
return task.Result;
}
catch(Exception e)
{
return false;
}
}
public async Task<bool> SIn()
{
var msaAuthProvider = new MsaAuthenticationProvider(
this.oneDriveClientId,
this.oneDriveReturnUrl,
this.scopes,
new CredentialVault(this.oneDriveClientId));
await msaAuthProvider.AuthenticateUserAsync();
driveClient = new OneDriveClient(this.oneDriveBaseUrl, msaAuthProvider);
}
Upvotes: 0
Views: 1159
Reputation: 5500
mm8 is right that not calling async from inside a sync method is the best way to solve your issue,
remember that the public async void EventHandler()
method was specifically designed for running long running tasks from a gui linked control
However it isn't always possible to rewrite an entire system to be async when only one small section needs changing
In this case you should avoid waiting for the results as this makes the async process pointless, what you can do though is break your synchronous code into 2 parts a before and after
ie
public async Task<string> GetData(int delay)
{
await Task.Delay(delay);
return "complete";
}
public void StartGettingData()
{
GetData(5000).ContinueWith(t => CompleteGetData(t.Result), TaskScheduler.Current);
}
public void CompleteGetData(string message)
{
UpdateStatus(message);
}
this method does have the added complexity of requiring you to ensure thread safety yourself, which is why the async/await functionality was introduced
Upvotes: 0
Reputation: 169390
Calling Wait()
blocks the UI thread which means that the continuation of SIn()
, i.e. the part that will eventually be executed once the Task
returned by AuthenticateUserAsync()
has completed, won't be able to execute on this thread. This results in a deadlock.
You may be able to get around this by avoiding capturing the context by calling ConfigureAwait(false)
in SIn()
:
public async Task<bool> SIn()
{
var msaAuthProvider = new MsaAuthenticationProvider(
this.oneDriveClientId,
this.oneDriveReturnUrl,
this.scopes,
new CredentialVault(this.oneDriveClientId));
await msaAuthProvider.AuthenticateUserAsync().ConfigureAwait(false);
driveClient = new OneDriveClient(this.oneDriveBaseUrl, msaAuthProvider);
}
But the "real" solution to this kind of issues is not to mix asynchronous and synchronous code, i.e. SignIn
should be asynchronous and await SIn()
. Don't block on asynchronous code by calling Wait()
or Result
:
public Task<bool> SignIn(User user)
{
try
{
...
return await SIn();
}
catch (Exception e)
{
return false;
}
}
Please refer to @Stephen Cleary's blog post for more information about this.
Upvotes: 1