[英]Task.Run using custom thread pool
我必須解決一個臨時情況,需要我做一個非理想的事情:我必須從同步中調用異步方法。
我只想在這里說,我知道我所遇到的所有問題,並且我理解為什么不建議這樣做的原因。
這就是說,我在處理大量的代碼庫,這是完全同步從上到下, 沒有辦法 ,我可以改寫一切使用異步地等待着一個合理的時間量。 但我確實需要重寫這個代碼庫的一些小部分來使用我在過去一年左右慢慢開發的新異步API,因為它有很多新功能,舊代碼庫可以從中受益好吧,但由於遺留原因無法得到它們。 由於所有代碼都不會很快消失,我面臨着一個問題。
TL; DR:大型同步代碼庫不能輕易地重寫以支持異步,但現在需要調用另一個大型代碼庫,這是完全異步的。
我目前正在做同步代碼庫中最簡單的事情:將每個異步調用包裝到Task.Run
並以同步方式等待Result
。
這種方法的問題是,只要同步代碼庫在緊密循環中執行此操作,它就會變慢。 我理解原因並且我知道我能做什么 - 我必須確保所有異步調用都是在同一個線程上啟動而不是每次從線程池中借用一個新的異步調用(這就是Task.Run
一樣)。 這種借用和返回會導致很多切換,如果做得很多,可能會大大減慢速度。
除了編寫我自己的調度程序之外,還有什么選擇,而不是重用一個專用線程?
更新 :為了更好地說明我正在處理的內容,我提供了一個我需要做的最簡單轉換的例子(還有更復雜的轉換)。
它基本上是簡單的LINQ查詢,它使用自定義LINQ提供程序。 下面沒有EF或類似的東西。
[舊代碼]
var result = (from c in syncCtx.Query("Components")
where c.Property("Id") == id
select c).SingleOrDefault();
[新規范]
var result = Task.Run(async () =>
{
Dictionary<string, object> data;
using (AuthorizationManager.Instance.AuthorizeAsInternal())
{
var uow = UnitOfWork.Current;
var source = await uow.Query("Components")
.Where("Id = @id", new { id })
.PrepareAsync();
var single = await source.SingleOrDefaultAsync();
data = single.ToDictionary();
}
return data;
}).Result;
如上所述,這是一個不那么復雜的例子,它已經包含2個異步調用。
更新2:我試圖消除Task.Run
和調用.Result
直接在一個包裝異步方法的結果,通過@Evk和@Manu的建議。 不幸的是,在我的暫存環境中進行測試時,我很快遇到了僵局。 我仍然試圖了解究竟發生了什么,但顯然在我的情況下不能簡單地刪除Task.Run
。 還有其他要解決的問題,首先......
我認為你沒有走上正軌。 在Task.Run
包裝每個異步調用對我來說似乎很可怕,它總是啟動一個你不需要的額外任務。 但我明白在大型代碼庫中引入async / await可能會有問題。
我看到了一個可能的解決方案:將所有異步調用解壓縮到單獨的異步方法中。 這樣,您的項目將具有從同步到異步的非常好的轉換,因為您可以逐個更改方法而不會影響代碼的其他部分。
像這樣的東西:
private Dictionary<string, object> GetSomeData(string id)
{
var syncCtx = GetContext();
var result = (from c in syncCtx.Query("Components")
where c.Property("Id") == id
select c).SingleOrDefault();
DoSomethingSyncWithResult(result);
return result;
}
會變成這樣的:
private Dictionary<string, object> GetSomeData(string id)
{
var result = FetchComponentAsync(id).Result;
DoSomethingSyncWithResult(result);
return result;
}
private async Task<Dictionary<string, object>> FetchComponentAsync(int id)
{
using (AuthorizationManager.Instance.AuthorizeAsInternal())
{
var uow = UnitOfWork.Current;
var source = await uow.Query("Components")
.Where("Id = @id", new { id })
.PrepareAsync();
var single = await source.SingleOrDefaultAsync();
return single.ToDictionary();
}
}
由於您處於Asp.Net環境中,因此將同步與異步混合是一個非常糟糕的主意 。 我很驚訝您的Task.Run
解決方案適合您。 將新的異步代碼庫合並到舊的同步代碼庫中的次數越多,遇到的問題就越多,除了以異步方式重寫所有內容之外,沒有簡單的解決方法。
我強烈建議您不要將異步部分混合到同步代碼庫中。 相反,從“自下而上”工作, 將所有內容從同步更改為異步,您需要等待異步調用 。 這可能看起來很多工作,但是比現在搜索一些“黑客”並且不解決下划線問題的好處要高得多。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.