簡體   English   中英

阻止收集過程一次n個項目 - 一旦完成,就會繼續

[英]blocking collection process n items at a time - continuing as soon as 1 is done

我有以下場景。

  1. 我從數據庫中將50個作業轉換為阻塞集合。

  2. 每項工作都是一項長期工作。 (可能是)。 所以我想在一個單獨的線程中運行它們。 (我知道 - 將它們作為Task.WhenAll運行並讓TPL解決它可能會更好 - 但我想控制同時運行多少次)

  3. 說我想同時運行其中的5個(可配置)

  4. 我創建了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();
}

您可以使用SemaphoreSlim執行此操作,例如在此答案中 ,或使用ForEachAsync如此答案中所示

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM