[英]Running Multiple Async Tasks - Faster results Chaining vs Parallel?
我想同時進行7個不同的API調用,並且希望每個完成后都可以更新UI。
我一直在擺弄兩種不同的方法,將請求鏈接起來,並同時(並行)關閉所有請求。
兩者似乎都可以工作,但是由於某種原因,我的並行任務比鏈接它們花費的時間長得多。
我是TPL /並行技術的新手,所以可能是我的代碼不正確,但是由於每個請求都必須在下一次啟動之前完成,因此鏈接請求是否需要花費更長的時間? 而不是並行運行,它們都立即消失了,所以您只需要等待最慢的時間?
如果您看到我的邏輯或代碼中的錯誤,請告訴我。 我對收到的回復時間感到滿意,但我不明白為什么。
我的“鏈接”代碼:
await (Task.Run(() => WindLookup_DoWork()).
ContinueWith((t) => WindLookup_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith((t) => OFAC_DoWork()).ContinueWith((t) => OFAC_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith((t) => BCEGS_DoWork()).ContinueWith((t) => BCEGS_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith((t) => BOPTerritory_DoWork()).ContinueWith((t) => BOPTerritory_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith((t) => TerrorismTerritory_DoWork()).ContinueWith((t) => TerrorismTerritory_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith((t) => ProtectionClass_DoWork()).ContinueWith((t) => ProtectionClass_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith((t) => AddressValidation_DoWork()).ContinueWith((t) => AddressValidation_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()));
我的“並行”代碼:
List<Task> taskList = new List<Task>();
taskList.Add(Task.Run(() => WindLookup_DoWork()).
ContinueWith((t) => WindLookup_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()));
taskList.Add(Task.Run(() => BCEGS_DoWork()).
ContinueWith((t) => BCEGS_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()));
taskList.Add(Task.Run(() => BOPTerritory_DoWork()).
ContinueWith((t) => BOPTerritory_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()));
taskList.Add(Task.Run(() => TerrorismTerritory_DoWork()).
ContinueWith((t) => TerrorismTerritory_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()));
taskList.Add(Task.Run(() => ProtectionClass_DoWork()).
ContinueWith((t) => ProtectionClass_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()));
taskList.Add(Task.Run(() => OFAC_DoWork()).
ContinueWith((t) => OFAC_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()));
taskList.Add(Task.Run(() => AddressValidation_DoWork()).
ContinueWith((t) => AddressValidation_ProcessResults(), TaskScheduler.FromCurrentSynchronizationContext()));
await Task.WhenAll(taskList.ToArray());
我基本上已經轉換了舊的背景工作程序代碼,這就是為什么有DoWork方法和更新UI的“回調”方法的原因。
DoWork方法調用API的POST方法,並且處理結果僅用響應xml填充文本區域。
我一直在擺弄兩種不同的方法,將請求鏈接起來,並同時(並行)關閉所有請求。
區分並發和並行很重要。 對於您的情況,您只想同時完成所有操作,這是一種並發形式。 並行是一種更具體的技術,它使用多個線程來實現並發,這適用於CPU密集型工作。 但是,在您的情況下,工作是受I / O約束的(API請求),在這種情況下,更合適的並發形式將是異步的。 異步是不使用線程的並發形式。
兩者似乎都可以工作,但是由於某種原因,我的並行任務比鏈接它們花費的時間長得多。
我不確定為什么它們會顯着更長,但是一個常見的問題是,同時請求的數量受到限制,無論是在客戶端( ServicePointManager.DefaultConnectionLimit
)還是在服務器端(例如,參見並發請求)和會話狀態 )。
如果您看到我的邏輯或代碼中的錯誤,請告訴我。
不幸的是,TPL很難從參考文檔或IntelliSense中學習,因為有太多的方法和類型只應在非常特殊的情況下使用。 特別是, 不要在您的方案中使用ContinueWith
; 它具有StartNew
所做的相同問題 (兩個鏈接均指向我的博客)。
更好(更可靠,更易於維護)的方法是引入一些輔助方法:
async Task WindLookupAsync()
{
await Task.Run(() => WindLookup_DoWork());
WindLookup_ProcessResults();
}
// etc. for the others
// Calling code (concurrent):
await Task.WhenAll(
WindLookupAsync(),
BCEGSAsync(),
BOPTerritoryAsync(),
TerrorismTerritoryAsync(),
ProtectionClassAsync(),
OFACAsync(),
AddressValidationAsync()
);
// Calling code (serial):
await WindLookupAsync();
await BCEGSAsync();
await BOPTerritoryAsync();
await TerrorismTerritoryAsync();
await ProtectionClassAsync();
await OFACAsync();
await AddressValidationAsync();
使用重構的代碼,就不需要ContinueWith
或顯式的TaskScheduler
。
但是,每個請求仍在為每個請求刻錄線程池線程。 如果這是台式機應用程序,那不是世界末日,但它沒有使用最好的解決方案。 正如我在回答開頭提到的那樣,更適合解決此問題的方法是異步而不是並行 。
要使代碼異步,您應該首先從POST API調用開始,然后將其更改為使用異步版本,然后使用await
調用。 (附帶說明: WebClient
確實具有異步方法,但是請考慮將HttpClient
更改為更自然地適合async
)。 一旦使用await
調用該POST API,這將要求_DoWork
方法變得異步(並返回Task
而不是void
)。 在這一點上,您可以更改上面的幫助程序方法以直接await
這些方法,而不是使用Task.Run
,例如:
async Task WindLookupAsync()
{
await WindLookup_DoWork();
WindLookup_ProcessResults();
}
調用代碼(並發和串行版本)保持不變。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.