簡體   English   中英

如何在同步方法中調用異步方法?

[英]How can I call an async method within a sync method?

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

調用Wait()阻止UI線程,這意味着SIn()的繼續SIn()AuthenticateUserAsync()返回的Task完成后最終將要執行的部分AuthenticateUserAsync()將無法在此線程上執行。 這將導致死鎖。

您可以通過在SIn()調用ConfigureAwait(false)來避免捕獲上下文,從而解決此問題:

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的博客文章 ,以獲取有關此信息的更多信息。

mm8是對的,不要從sync方法內部調用async是解決問題的最佳方法,

請記住, 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);
}

此方法的確增加了復雜性,要​​求您自己確保線程安全,這就是引入異步/等待功能的原因

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM