簡體   English   中英

如何使用多個 ActionBlock 並行化列表?

[英]How to parallelize a list using multiple ActionBlocks?

我有一個大型文件夾結構,我正試圖從共享驅動器下載。 共享驅動器很慢,但它也有幾個鏡像。 為了加快這個過程,我正在嘗試制作一個小的下載器應用程序來管理與所有慢速鏡像的並行連接。 單個文件將從不同的鏡像下載。 我還希望能夠限制一次連接到每個鏡像的線程數。 (這已經存在了嗎?那我就不用寫任何代碼了。不過我確實看過。)

這似乎可能是一個 Dataflow 用例,盡管我對 Dataflow 很陌生,所以我並不積極。 我從這樣的事情開始:

var buffer = new BufferBlock<string>();
var blockOptions = new ExecutionDataflowBlockOptions
{
    MaxDegreeOfParallelism = threadsPerPath
};

IEnumerable<ActionBlock<string>> blocks = mirrors.Select(basePath =>
{
    return new ActionBlock<string>(
        file => {
            string destinationFile = Path.Combine(destination, file);
            Directory.CreateDirectory(Path.GetDirectoryName(destinationFile));
            File.Copy(Path.Combine(basePath, file), destinationFile);
        },
        blockOptions);
});

foreach (ActionBlock<string> block in blocks)
{
    buffer.LinkTo(block);
}

await Task.Run(() =>
{
    string top = mirrors[0];

    int baseLength = top.Length;

    IEnumerable<string> allFiles = Directory.EnumerateFiles(top, "*", SearchOption.AllDirectories);

    foreach (string path in allFiles)
    {
        buffer.Post(path[baseLength..]);
    }

    buffer.Complete();
});

(我打算玩threadsPerPath 。不確定我是否會看到並行訪問同一個鏡像的好處。)運行時,它只使用第一個鏡像 - 據我所知,其他鏡像的 ActionBlocks 永遠不會獲取數據. 我認為這是設計使然,但我不知道該怎么做。 如何讓多個 ActionBlocks 並行處理同一個緩沖區,其中緩沖區中的每個項目只進入其中一個 ActionBlocks?

限制跨多個ActionBlock<T>的並行化的一種方法是使用同一ConcurrentExclusiveSchedulerPair實例的ConcurrentScheduler配置所有這些:

var sharedScheduler = new ConcurrentExclusiveSchedulerPair(
    TaskScheduler.Default, maxConcurrencyLevel: 50).ConcurrentScheduler;

var blockOptions = new ExecutionDataflowBlockOptions
{
    MaxDegreeOfParallelism = threadsPerPath,
    TaskScheduler = sharedScheduler
};

這僅在ActionBlock<T>的操作是同步的情況下才有效,因為它在您發布的代碼中。 您不能使用TaskScheduler選項限制異步工作的並發性。 在這種情況下,您必須在action中使用共享的SemaphoreSlim

要實現負載平衡,您需要限制鏈接塊的輸入容量。 如果要將請求傳播到所有塊,則必須限制它們的 BoundedCapacity,甚至可能限制為 1 或至少與MaxDegreeOfParallelism相同的數字

var blockOptions = new ExecutionDataflowBlockOptions
{
    MaxDegreeOfParallelism = threadsPerPath,
    BoundedCapacity=bound
};

原始代碼的問題是所有ActionBlock都有無限的輸入緩沖區,因此所有消息(文件)都在第一個塊中結束。

塊的 output 被發送到第一個可用的鏈接塊。 如果該塊具有無限緩沖區,則所有消息都將發送到同一個塊。 通過將所有塊的輸入緩沖區限制為僅一項,您可以強制將每條消息(在本例中為文件)發送到不同的塊。

發布消息后,您可以等待所有塊完成:

await Task.WhenAll(blocks.Select(b=>b.Completion));

暫無
暫無

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

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