我正在尝试在同步方法(SignIn)中调用异步任务(SIn)。我需要synch方法,因为我将ref传递给该方法。但是,当我调用异步任务时,GUI被冻结。异步任务是使用onedrive sdk的简单登录。
我试图等待任务,但是GUI仍然冻结。我也尝试过创建新的线程,但是它也没有用。如何调用异步方法?
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);
}
答案 0 :(得分:1)
调用Wait()
会阻止UI线程,这意味着SIn()
的延续,即Task
返回的AuthenticateUserAsync()
完成后将最终执行的部分,将无法在此线程上执行。这会导致死锁。
您可以通过避免在ConfigureAwait(false)
中调用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);
}
但是解决此类问题的“真正”解决方案不是混合异步代码和同步代码,即SignIn
应该是异步的并等待SIn()
。不要通过调用Wait()
或Result
来阻塞异步代码:
public Task<bool> SignIn(User user)
{
try
{
...
return await SIn();
}
catch (Exception e)
{
return false;
}
}
有关此信息,请参阅@Stephen Cleary的blog post。
答案 1 :(得分:0)
mm8是正确的,不从同步方法内部调用异步是解决问题的最佳方法,
请记住,public async void EventHandler()
方法是专门为从gui链接控件中运行长时间运行的任务而设计的
但是,当只有一个小部分需要更改时,并非总是可以将整个系统重写为异步的
在这种情况下,您应该避免等待结果,因为这会使异步过程变得毫无意义,但是您可以做的是将同步代码分为前后两部分
即
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);
}
此方法确实增加了复杂性,要求您自己确保线程安全,这就是引入异步/等待功能的原因