Pull to refresh

Comments 16

Зачем так долго и непонятно объяснять простую вещь? Вся проблема в двух словах:


ActionBlock выполняет задачу, которая ставит другие задачи в тот же самый ActionBlock. В итоге, задача может заблокировать свое же выполнение. А если что-то плохое может случиться — оно обязательно случится.


PS правильно в такой ситуации не ставить дочерние задачи в тот же самый ActionBlock, а вычислять их рекурсивно.

Я могу описать проблему еще проще: причина дедлока в наличии join-а потоков. Но ведь это малоинформативно и не очень-то дает понять, почему ожидаемый поток все еще заблокирован.

И нет, тут все сложнее, чем «своя задача блокирует свое исполнение». SendAsync реализован несколько сложнее: на самом деле, это source dataflow block с буфером в один элемент и TaskCompletionSource-ом, который переводит нежележащую таску в завершенное состояние, когда целевой блок (в данном случае — ActionBlock) решается на обработку элемента от него.

И нет, вычислять рекусривно задачи по месту — это не вариант, поскольку в этом случае Degree of parallelism пойдет лесом. Представьте себе, что зависимости между файлами — это дерево. Так вот, дерево может быть (и, как вы сказали по поводу «плохого») или наверняка будет не сбаллансированным. Это значит, что использование этого подхода приведет к тому, что мы останемся с одной задачей, которая будет долго и упорно парсить огромную ветку дерева. Нехорошо это:)

Это всего лишь означает, что ActionBlock плохо справляется с распараллеливанием рекурсивного алгоритма. Но отталкиваться нужно именно от алгоритма, а не от инструмента.

Нисколько с этим не спорю. Проблема лишь в том, что проблема ActionBlock-а не очевидна.

Давайте, например, посмотрим, как может приниматься решение об использовании того или иного инструмента длярешения определенной проблемы.

Дана проблема: проанализировать набор файлов с возможными зависимостями.
Возможные решения: ActionBlock, ConcurrentCollection + TPL, что-то свое.

Даже при довольно серьезном анализе, ActionBlock кажется одним из лучших решений, поскольку он контролирует конкурентность, позволяет задавать границу, удобен в использовании, поддерживается одной небольшой компанией и используется сотнями тысяч программистов.

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

Что-то я не понимаю, как этот самый "серьезный анализ" мог обойти название библиотеки? Очевидно же, что если задача не представляется в виде потока данных — то Tpl Dataflow является не лучшим вариантом.

Ну, можно почитать, что такое ActionBlock и понять, что подобная задача является выражденным случаем датафлоу.

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

Или вы хотите сказать, что никогда не выибрали инструмент, который потом оказывался неподходящим? Возможно:), я не такой. И после своей ошибки я решил поделиться своим опытом. Если у вас и с этим какое-то несогласие, то я не совсем понимаю, чего вы хотите добиться своими комментариями;)
Как то Thread.Sleep(10); вместо Task.Delay(10) в ассинхроноом коде смотрится чужеродно.
Кстати, лучший способ — использовать SpinWait.Spin(), он реально будет нагружать процессор, в отличие от Sleep-а, который будет блокировать таску. Но в данном случае блокировка, а не yield управления из таски имеет смысл.
Ну, код не совсем продакшн качества, так что в этом случае — это не сильно важно.
Тогда уж так даже более понятно
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),
                });
        }
Это не эквивалентный код. Ваш код сразу вернёт таск, который при выполнении будет приостановлен на 10 миллисекунд, а код в посте будет сначала приостановлен на 10 миллисекунд, а затем вернёт таск.
Ну, это просто не важно:). Это не настоящий парсинг, а фейковый, любой вариант будет ок;)
UFO just landed and posted this here
Ссылка под статьей, ищите «seteplia»
Вы, конечно, имели в виду английский, но комментарий зачётен, да!
Sign up to leave a comment.

Articles