如何在同步方法中调用异步方法?

时间:2019-08-29 12:40:32

标签: c# wpf async-await task

我正在尝试在同步方法(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);
}

2 个答案:

答案 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链接控件中运行长时间运行的任务而设计的

但是,当只有一个小部分需要更改时,并非总是可以将整个系统重写为异步的

在这种情况下,您应该避免等待结果,因为这会使异步过程变得毫无意义,但是您可以做的是将同步代码分为前后两部分

  • before方法将准备并启动任务,
  • 事后处理结果

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);
}

此方法确实增加了复杂性,要​​求您自己确保线程安全,这就是引入异步/等待功能的原因