Как правильно запускать несколько задач async параллельно?

Что делать, если вам нужно запускать несколько задач асинхронного ввода-вывода параллельно, но вам нужно убедиться, что одновременно выполняются не более X процессов ввода-вывода? а задачи обработки до и после ввода-вывода не должны иметь такого ограничения.

Вот сценарий - скажем, есть 1000 задач; каждый из них принимает текстовую строку в качестве входного параметра; преобразует этот текст (предварительная обработка ввода-вывода), а затем записывает преобразованный текст в файл. Цель состоит в том, чтобы сделать логику предварительной обработки использовать 100% процессоров/ядер и операций ввода-вывода для задач, выполняемых с максимальным значением 10 градусов parallelism (max 10 одновременно открыт для записи файлов за раз).

Можете ли вы предоставить пример кода, как это сделать с помощью С#/.NET 4.5?

http://blogs.msdn.com/b/csharpfaq/archive/2012/01/23/using-async-for-file-access-alan-berman.aspx

Ответ 1

Я думаю, что использование TPL Dataflow для этого было бы хорошей идеей: вы создаете блоки до и после обработки с неограниченным parallelism, файловым блоком с ограниченным parallelism и свяжете их вместе. Что-то вроде:

var unboundedParallelismOptions =
    new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded
    };

var preProcessBlock = new TransformBlock<string, string>(
    s => PreProcess(s), unboundedParallelismOptions);

var writeToFileBlock = new TransformBlock<string, string>(
    async s =>
            {
                await WriteToFile(s);
                return s;
            },
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 10 });

var postProcessBlock = new ActionBlock<string>(
    s => PostProcess(s), unboundedParallelismOptions);

var propagateCompletionOptions =
    new DataflowLinkOptions { PropagateCompletion = true };

preProcessBlock.LinkTo(writeToFileBlock, propagateCompletionOptions);
writeToFileBlock.LinkTo(postProcessBlock, propagateCompletionOptions);

// use something like await preProcessBlock.SendAsync("text") here

preProcessBlock.Complete();
await postProcessBlock.Completion;

Где WriteToFile() может выглядеть так:

private static async Task WriteToFile(string s)
{
    using (var writer = new StreamWriter(GetFileName()))
        await writer.WriteAsync(s);
}

Ответ 2

Похоже, вы захотите рассмотреть Djaphstra Semaphore, чтобы контролировать доступ к запуску задач.

Однако это звучит как типичная проблема с очередью/фиксированным числом потребителей, которая может быть более подходящим способом ее структурирования.