Comments 16
Зачем так долго и непонятно объяснять простую вещь? Вся проблема в двух словах:
ActionBlock выполняет задачу, которая ставит другие задачи в тот же самый ActionBlock. В итоге, задача может заблокировать свое же выполнение. А если что-то плохое может случиться — оно обязательно случится.
PS правильно в такой ситуации не ставить дочерние задачи в тот же самый ActionBlock, а вычислять их рекурсивно.
И нет, тут все сложнее, чем «своя задача блокирует свое исполнение». SendAsync реализован несколько сложнее: на самом деле, это source dataflow block с буфером в один элемент и TaskCompletionSource-ом, который переводит нежележащую таску в завершенное состояние, когда целевой блок (в данном случае — ActionBlock) решается на обработку элемента от него.
И нет, вычислять рекусривно задачи по месту — это не вариант, поскольку в этом случае Degree of parallelism пойдет лесом. Представьте себе, что зависимости между файлами — это дерево. Так вот, дерево может быть (и, как вы сказали по поводу «плохого») или наверняка будет не сбаллансированным. Это значит, что использование этого подхода приведет к тому, что мы останемся с одной задачей, которая будет долго и упорно парсить огромную ветку дерева. Нехорошо это:)
Это всего лишь означает, что ActionBlock плохо справляется с распараллеливанием рекурсивного алгоритма. Но отталкиваться нужно именно от алгоритма, а не от инструмента.
Давайте, например, посмотрим, как может приниматься решение об использовании того или иного инструмента длярешения определенной проблемы.
Дана проблема: проанализировать набор файлов с возможными зависимостями.
Возможные решения: ActionBlock, ConcurrentCollection + TPL, что-то свое.
Даже при довольно серьезном анализе, ActionBlock кажется одним из лучших решений, поскольку он контролирует конкурентность, позволяет задавать границу, удобен в использовании, поддерживается одной небольшой компанией и используется сотнями тысяч программистов.
В результате, он является вполне адекватным выбором и лишь люди, знающие хорошо его внутреннее устройство смогут сказать сразу же, что у него будут проблемы при обработке древовидных структур данных.
Что-то я не понимаю, как этот самый "серьезный анализ" мог обойти название библиотеки? Очевидно же, что если задача не представляется в виде потока данных — то Tpl Dataflow является не лучшим вариантом.
Я как-то несколько запутался и не понял, что мы обсуждаем? Что было очевидно с самого начала, что тул не подходит? Возможно, но если вы не поленитесь и погуглите на гитхабе или в других общедоступных репах сценарии использования ActionBlock-ов, то найдете, что они используются в большом числе сценариев.
Или вы хотите сказать, что никогда не выибрали инструмент, который потом оказывался неподходящим? Возможно:), я не такой. И после своей ошибки я решил поделиться своим опытом. Если у вас и с этим какое-то несогласие, то я не совсем понимаю, чего вы хотите добиться своими комментариями;)
Если надо сымитировать вычисления — то в самый раз.
private static async Task<ParsedFile> ParseFileAsync(string path)
{
Console.WriteLine($"Parsing '{path}'. {{0}}",
$"Thread Id - {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(10);
return new ParsedFile()
{
FileName = path,
Dependencies = GetFileDependencies(path),
});
}
Изучаем ActionBlock: или короткая история о противном дедлоке