[英]blocking collection process n items at a time - continuing as soon as 1 is done
我有以下場景。
我從數據庫中將50個作業轉換為阻塞集合。
每項工作都是一項長期工作。 (可能是)。 所以我想在一個單獨的線程中運行它們。 (我知道 - 將它們作為Task.WhenAll運行並讓TPL解決它可能會更好 - 但我想控制同時運行多少次)
說我想同時運行其中的5個(可配置)
我創建了5個任務(TPL),每個作業一個並行並行運行。
我想要做的是,一旦第4步中的一個作業完成,就立即在阻塞集合中選擇下一個作業,並繼續進行直到完成所有50個作業。
我正在考慮創建一個Static blockingCollection和一個TaskCompletionSource,它將在作業完成時調用,然后它可以再次調用使用者從隊列中一次選擇一個作業。 我還想在每項工作上調用async / await - 但這是最重要的 - 不確定這是否對該方法有影響。
這是完成我想要做的事情的正確方法嗎?
與此鏈接類似,但是捕獲的是我想在前N個項目中的一個完成后立即處理下一個作業。 畢竟N都沒有完成。
更新:
好的,如果有人想在以后使用它,我的代碼片段正是我想要的。 如下所示,創建了5個線程,每個線程在完成當前操作時啟動下一個作業。 在任何給定時間只有5個線程處於活動狀態。 我知道這可能不會像這樣100%工作,並且如果與一個cpu / core一起使用,將會出現上下文切換的性能問題。
var block = new ActionBlock<Job>(
job => Handler.HandleJob(job),
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 5 });
foreach (Job j in GetJobs())
block.SendAsync(j);
工作2開始於主題:13。 等待時間:3600000ms。 時間:2014年8月29日下午3:14:43
工作4開始於主題:14。 等待時間:15000ms。 時間:2014年8月29日下午3:14:43
作業0在線程上開始:7。 等待時間:600000ms。 時間:2014年8月29日下午3:14:43
作業1開始於主題:12。 等待時間:900000ms。 時間:2014年8月29日下午3:14:43
工作3開始於主題:11。 等待時間:120000ms。 時間:2014年8月29日下午3:14:43
工作4完成了主題:14。 2014年8月29日下午3:14:58
作業5開始於主題:14。 等待時間:1800000ms。 時間:2014年8月29日下午3:14:58
工作3完成線程:11。 2014年8月29日下午3:16:43
Job 6從線程開始:11。 等待時間:1200000ms。 時間:2014年8月29日下午3:16:43
作業0完成了主題:7。 2014年8月29日下午3:24:43
Job 7從線程開始:7。 等待時間:30000ms。 時間:2014年8月29日下午3:24:43
工作7完成線程:7。 2014年8月29日下午3:25:13
Job 8開始於主題:7。 等待時間:100000ms。 時間:2014年8月29日下午3:25:13
作業8完成了主題:7。 2014年8月29日下午3:26:53
Job 9從線程開始:7。 等待時間:900000ms。 時間:2014年8月29日下午3:26:53
工作1完成線程:12。 2014年8月29日下午3:29:43
作業10開始於主題:12。 等待時間:300000ms。 時間:2014年8月29日下午3:29:43
作業10完成了主題:12。 8/29/2014 3:34:43 PM
工作11開始於主題:12。 等待時間:600000ms。 時間:2014年8月29日下午3:34:43
工作6完成線程:11。 2014年8月29日下午3:36:43
工作12開始於主題:11。 等待時間:300000ms。 時間:2014年8月29日下午3:36:43
工作12完成了主題:11。 2014年8月29日下午3:41:43
Job 13開始於主題:11。 等待時間:100000ms。 時間:2014年8月29日下午3:41:43
作業9完成了主題:7。 2014年8月29日下午3:41:53
Job 14從線程開始:7。 等待時間:300000ms。 時間:2014年8月29日下午3:41:53
工作13完成了主題:11。 2014年8月29日下午3:43:23
工作11完成了主題:12。 2014年8月29日下午3:44:43
工作5完成線程:14。 8/29/2014 3:44:58 PM
作業14完成了主題:7。 8/29/2014 3:46:53 PM
工作2完成了主題:13。 8/29/2014 4:14:43 PM
您可以使用TPL Dataflow
輕松實現所需。
你可以做的是使用BufferBlock<T>
,它是一個用於存儲數據的緩沖區,並將它與ActionBlock<T>
鏈接在一起,當它們從BufferBlock<T>
進入時將消耗這些請求。
現在,這里的美妙之處在於,您可以指定ActionBlock<T>
使用ExecutionDataflowBlockOptions
類並發處理的請求數。
這是一個簡化的控制台版本,在它們進入時處理一堆數字,打印它們的名字和Thread.ManagedThreadID
:
private static void Main(string[] args)
{
var bufferBlock = new BufferBlock<int>();
var actionBlock =
new ActionBlock<int>(i => Console.WriteLine("Reading number {0} in thread {1}",
i, Thread.CurrentThread.ManagedThreadId),
new ExecutionDataflowBlockOptions
{MaxDegreeOfParallelism = 5});
bufferBlock.LinkTo(actionBlock);
Produce(bufferBlock);
Console.ReadKey();
}
private static void Produce(BufferBlock<int> bufferBlock)
{
foreach (var num in Enumerable.Range(0, 500))
{
bufferBlock.Post(num);
}
}
如果需要,您還可以使用等待的BufferBlock.SendAsync
異步發布它們
這樣,您可以讓TPL
為您處理所有限制,而無需手動執行。
您可以使用BlockingCollection
,它可以正常工作,但它是在async-await
之前構建的,因此它會同步阻塞,這在大多數情況下可能會降低。
Yuval Itzchakov建議你最好使用async
ready TPL Dataflow
。 所有你需要的是一個ActionBlock
,它同時處理每個項目的MaxDegreeOfParallelism
為5,你同步發布你的工作( block.Post(item)
)或異步( await block.SendAsync(item)
):
private static void Main()
{
var block = new ActionBlock<Job>(
async job => await job.ProcessAsync(),
new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = 5});
for (var i = 0; i < 50; i++)
{
block.Post(new Job());
}
Console.ReadKey();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.