簡體   English   中英

如何處理在一個平台上同步但在另一個平台上異步的代碼

[英]How to handle code that is synchronous on one platform but async on another

在編寫基於Xamarin的跨平台C#應用程序時,我經常遇到的情況是在一個平台上同步但在另一個平台上異步的代碼。 例如,文件訪問在iOS,Android和Mac上是同步的,但在Windows上是異步的。

這導致了一些棘手的問題。 首先,在純編碼級別上,編譯器希望方法是異步還是不同步。

但它比這更糟糕。 例如,如果iOS UI線程發現它需要加載一個小文件,並且同步地在UI線程上這樣做,這可能不會對用戶造成任何明顯的打嗝。 是的,我意識到,作為一般規則,人們希望預先加載這些資源。 但如果不發生這種情況,並且文件很小,則不會導致問題。

但是,如果從iOS UI代碼調用C#異步方法並等待結果,應用程序將掛起。

結果是我們有兩個方法,Foo()和FooAsync(),我們調用哪個方法取決於平台。 但我們不能只讓整個事情變得異步。

鑒於此,如何組織一個代碼盡可能地堅持DRY,同時在一個平台上保持同步並在另一個平台上保持同步?

**

編輯 - 響應一個例子的請求:這里有一些虛擬代碼來說明問題。 如果有人打電話

 await StringLoaders.ReadAllText(somePath) 

從iOS UI線程,結果是掛起。 是的,這個特定的例子有解決方法。 但一般問題是反復出現的問題。 如果應用程序有很多層,那就特別糟糕 - 就像這樣 - 可能需要在整個代碼中攜帶同步/異步區別,從而產生大量方法的兩個版本。

public class AsyncStringLoader
{
  public async Task<string> ReadAllTextAsync(string path) {
    return await Task.Run(() => "Hello");
  }
}
public class StringLoader {
  public string ReadAllText(string path) {
    return "Hello";
  }
}
public static class StringLoaders {
  public static object Current { get; set;} // already trouble here -- we can't really have a proper interface for ReadAllText.
  public static async Task<string> ReadAllText(string path) {
    object loader = StringLoaders.Current;
    if (loader is StringLoader) {
      return ((StringLoader)loader).ReadAllText(path);
    } else if (loader is AsyncStringLoader) {
      return await ((AsyncStringLoader)loader).ReadAllTextAsync(path);  // DRY violation, in addition to the hang
    } else {
      throw new Exception("Unknown loader!");
    }
  }
}

如果你想要一個真實的例子,而不是一個愚蠢的例子,從NuGet下載PCLStorage庫,然后嘗試將它用於從iOS UI線程調用的任何東西。

您可以在頂層定義一個接口(或接口集),該接口僅包含異步方法以保持一致性。 此頂級接口將由更高級別的應用程序/ UI代碼使用,因此更高級別的代碼將該接口對象作為依賴項。 在該接口下面實現了每個操作系統類型的C#適配器,它們實現了異步接口方法。 對於Windows,您可以直接使用本機異步Windows API。 對於不提供異步api的其他操作系統,請使用其同步方法,但返回一個等待已計算結果的等待任務對象,如下所示:

return Task.FromResult(/*the result of the sync api call from iOS*/);

使用接口的更高級代碼將等待接口的異步方法,而不用擔心下面的實現。 如果底層實現是iOS,則調用將同步運行,就像您直接調用sync方法一樣。 如果底層實現是Windows,則將運行異步方法。

我建議將接口全部用於異步方法(與所有同步)的原因是,使同步調用看起來像async(通過Task.FromResult)比強制異步調用同步工作更安全,這可能導致死鎖。 另一個原因是,顯然您希望盡可能多地利用async api,至少對於提供異步方法的操作系統。

您可以根據配置參數等確定接口的哪個實際具體實現(即Windows,IOS等)在運行時傳遞到更高級別的代碼。您還可以使用現有的依賴注入容器之一用於向接口注冊具體類。

您的問題有點開放,如果您正在尋找其他內容,請提供更多詳細信息。

暫無
暫無

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

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