[英]Contractual async and sync code
有很多問題詢問是否要混合異步代碼和同步代碼。
大多數答案都說,為異步方法公開同步包裝,為同步方法公開異步包裝是一個壞主意。
但是,沒有一個答案能解決您必須混合使用異步和同步代碼的特定情況,以及如何避免由於該問題而引起的常見陷阱。
請參見以下示例:
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的用戶又如何呢?由於假設他們已經異步運行而不再進行優化,該怎么辦?
這是混合異步和同步的正確方法嗎?
通常是的。 如果您有一個公共接口,則其中的某些實現將能夠提供同步的實現,而其他實現僅是異步的,則返回Task<T>
是有意義的。 一個簡單的return Task.FromResult
可能是合適的。 但是也有例外,請參見下文。
希望syncSig運行異步但運行同步的用戶呢?
接口的某些方面無法用代碼表示。
如果您的IContract
要求它在返回Task
之前不阻塞調用線程的時間不超過X毫秒(以這種確切形式,這不是一個合理的硬性要求,但是您已經掌握了基本思想),並且無論如何它都會阻塞該線程,這是違反合同的行為,用戶應將其作為錯誤報告給實施了syncSig的任何人。
如果IContract
要求不引發同步異常,則必須使用Task.FromException
或等效方法報告任何錯誤,而syncSig仍然引發同步異常,這也違反了合同。
除此之外,如果syncSig只是立即返回正確的結果,那么沒有理由為什么會打擾任何用戶。
可以通過異步運行來優化syncSig的用戶又如何呢?由於假設他們已經異步運行而不再進行優化,該怎么辦?
如果syncSig同步運行不會引起任何問題,那么沒關系,還不需要進行優化。
如果同步運行的syncSig確實引起了問題,則基本調試工具應盡快告知開發人員syncSig引起了問題,應進行調查。
但是,沒有一個答案能解決您必須混合使用異步和同步代碼的特定情況,以及如何避免由於該問題而引起的常見陷阱。
那是因為這是一個壞主意。 任何混合代碼本質上都應該是完全臨時的,僅在過渡到異步API時才存在。
就是說,我已經寫了整篇文章 。
如何在避免常見陷阱的情況下混合合同同步代碼和異步代碼?
正如我在異步接口博客文章中所描述的那樣, async
是實現細節。 任務返回方法可以同步完成。 但是,我建議避免阻塞實現。 如果由於某種原因無法避免它們,那么我會仔細記錄它們的存在。
另外,您可以使用Task.Run
作為實現; 我不建議這樣做(原因詳細的在我的博客 ),但如果你必須有一個阻擋實現它是一個選項。
希望syncSig運行異步但運行同步的用戶呢?
調用線程被同步阻止。 這通常只是UI線程的問題-參見下文。
可以通過異步運行來優化syncSig的用戶又如何呢?由於假設他們已經異步運行而不再進行優化,該怎么辦?
如果調用代碼是ASP.NET,則應直接調用它並await
。 同步代碼將被同步執行,這在該平台上是理想的。
如果調用代碼是UI應用程序,則需要知道存在同步實現,並且可以將其包裝在Task.Run
。 這樣可以避免阻塞UI線程,而不管實現是同步還是異步。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.