2016-12-30 251 views
1

有很多問題詢問是否混合使用異步代碼和同步代碼。合同異步和同步代碼

大多數答案認爲爲異步方法公開同步包裝併爲同步方法公開異步包裝是一個壞主意。

但是,沒有一個答案能夠解決您需要混合異步代碼和同步代碼的特定情況,以及如何避免因此而出現的常見缺陷。

請看下面的例子:

class Program 
{ 
    static void Main(string[] args) 
    { 
     IContract signatory = new SyncSignatory(); 
     signatory.FullfillContractAsync().Wait(); 
     signatory = new AsyncSignatory(); 
     signatory.FullfillContractAsync().Wait(); 
    } 
} 

using System.Threading.Tasks; 

interface IContract 
{ 
    Task FullfillContractAsync(); 
} 

using System.Threading.Tasks; 

class AsyncSignatory : IContract 
{ 
    public async Task FullfillContractAsync() 
    { 
     await Task.Delay(5000); 
    } 
} 

using System.Threading; 
using System.Threading.Tasks; 

class SyncSignatory : IContract 
{ 
    public Task FullfillContractAsync() 
    { 
     Thread.Sleep(5000); 
     return Task.FromResult<object>(null); 
    } 
} 

或者:

using System.Threading; 
using System.Threading.Tasks; 

class SyncSignatory : IContract 
{ 
    public Task FullfillContractAsync() 
    { 
     return Task.Run(() => Thread.Sleep(5000)); 
    } 
} 

在此示例中,SyncSignatory和AsyncSignatory表示兩個可互換的類,因爲它們執行類似的功能,但它們以不同的方式執行這些功能 - 同步和異步。

如何在避免常見陷阱情況下混合契約同步和異步代碼?

如果用戶期望syncSig運行異步,但它運行同步?

如果用戶可以通過異步運行來優化syncSig,但不再更長,這是因爲他們認爲它已經運行了異步?

+0

@DanWilson修訂。 –

+0

我真的不知道這個問題。消費者需要了解的唯一方法是其參數,名稱和返回類型。這種方法在內部對內部調用者應該沒有什麼影響 – Jonesopolis

+0

我認爲如果沒有特定的用例,您的示例中目前太泛化了,我不認爲您能夠得到答案。誰會調用這些方法?你認爲這是你的代碼的熱門路線嗎? –

回答

1

但是,沒有一個答案能夠解決您需要混合使用異步代碼和同步代碼的特定場景,以及如何避免由於此問題而導致的常見缺陷。

這是因爲這是一個壞主意。任何混合代碼本質上都應該是完全臨時的,僅在向異步API轉換時才存在。

這就是說,我寫了an entire article on the subject

如何在避免常見陷阱情況下混合契約同步和異步代碼?

正如我在我的blog post on async interfaces中所描述的,async是一個實現細節。任務返回方法可能會同步完成。但是,我建議避免阻止的實現;如果由於某種原因無法避免,那麼我會仔細記錄它們的存在。

或者,您可能使用Task.Run作爲實現;我不建議這樣做(原因detailed on my blog),但如果您的有阻止實施,則這是一個選項。

如果用戶期望syncSig運行異步,但它運行同步?

調用線程被同步阻塞。這通常只是UI線程的問題 - 見下文。

如果用戶可以通過異步運行來優化syncSig,但不再更長,因爲他們認爲它已經運行了異步?

如果調用代碼是ASP.NET,那麼它應該直接調用它並且await。同步代碼將同步執行,這在該平臺上是可取的。

如果調用代碼是一個UI應用程序,那麼它需要知道有同步實現,並可以調用它包裝在Task.Run中。這可以避免阻塞UI線程,無論實現是同步還是異步。

3

這是混合異步和同步的正確方法嗎?

大部分是的。如果你有一個通用接口,其中一些實現將能夠提供同步實現,但其他實現只能是異步接口,返回Task<T>是有意義的。簡單的return Task.FromResult可能是合適的。但也有例外,請參閱下文。

如果用戶期望syncSig運行異步,但它運行同步?

接口的某些方面無法用代碼表示。

如果它沒有返回一個Task之前阻塞調用線程,超過X毫秒的時間你IContract的要求(在這個確切形式不是一個合理的硬性要求,但你的基本的想法),並且它阻止無論如何,這是一個違反合同的行爲,用戶應該將其報告爲執行syncSig的人的錯誤。

如果您的IContract要求它不拋出同步異常,使用Task.FromException或同等報告報告任何錯誤,並且您的syncSig拋出異步異常,那也是合同違規。

除此之外,如果syncSig只是立即返回正確的結果,那麼沒有理由爲什麼這會打擾任何用戶。

如果用戶可以通過異步運行來優化syncSig,但不再更長,因爲他們認爲它已經運行了異步?

如果syncSig同步運行不會導致任何問題,那沒關係,它不需要進行優化。

如果syncSig同步運行確實會導致問題,那麼基本的調試工具應該很快告訴開發人員syncSig正在引發問題並應該進行調查。

+0

我連接的第一個場景如何避免比第二個場景更多的陷阱? (在SyncSignatory同步中運行同步代碼並對其進行異步運行)。 –

+0

@FranciscoAguilera我在你的問題中沒有看到任何鏈接,我無法從你的評論中猜出你的意思。 – hvd

+0

哦,它不是一個鏈接,它是在問題底部編輯更多代碼。用異步代碼包裝它還是保持同步代碼? –