Pull to refresh

Comments 11

Маленький нюанс:
TResult result = highCpuFunc();
Это вполне себе синхронный вызов, который сводит на нет все старание вокруг него.
Мы ведь должны сначала вернуть tcs.Task, а потом, где-то в другой вселенной, вызвать tcs.SetResult(result);.
В противном случае все это не имеет смысла. Или я что-то пропустил и не прав?
Все верно. Собственно об этом и речь (проблема №1 из статьи): ожидание (await'e) некоторого метода, который может работать в синхронном режиме. И это может произойти (да что там — происходит) в любом месте цепочки вызовов.
Ответ очевиден: если задача интенсивная по вычислениям, тогда
где-то в другой вселенной, вызвать tcs.SetResult(result)

Безусловно. Но мы же рассматриваем evil code, не так ли?
Злой код — это одно. Но в вашем примере мы в любом случае сначала вызываем tcs.SetX, а уже потом return tcs.Task;
Т.е. ComputeAsync выполняется всегда синхронно.
Тогда давайте отделим вопрос синхронного вызова и установки SetResult до возвращения return tcs.Task.
Если стоит второй вопрос, то даже при вызове SetResult хоть в отдельном потоке установка результата возможна (на самом деле) еще до возвращения return tcs.Task. Пример был упрощен специально дабы проблему №2 (изменения состояния тасков) показать.
CPU-bound задачи вообще никогда не следует заворачивать в асинхронные функции типа ComputeAsync. Если эта задача выполняется при обработке запроса в нагруженном веб-сервисе, то выгоднее вызвать HighCpuFunc() напрямую и синхронно, ибо нет никакого смысла создавать новый поток и переключаться в него. Если же задача выполняется в пользовательском приложении с UI, и нужно позаботиться об отзывчивости, то это задача приложения вызвать Task.Run(() => HighCpuFunc()).

Сигнатура функции типа ComputeAsync() предполагает, что в ней делается I/O-bound задача, и использовать её для CPU-bound — это вводить пользователей этой функции в заблуждение, чреватое серьёзными проблемами.
Я того же мнения. Имхо, это косяк посерьёзнее забытых catch
Из метода вернется уже завершенный таск. Вы правы в случае истинной асинхронности, в статье описывается использование TBAP в синхронном коде.
  1. «пост», «таск», «скоуп», «паттерн», «фреймфорк», «недетерминированный» я бы заменил на русские слова, мы же для русских тут пишем. Заодним исправьте «итогого» на «итогового».
  2. В разделе «Истоки», где якобы всё объясняется, написано много непонятного. Например, что такое «вызов таска»? Или «Таск может выполнится в том же потоке» — странная фраза учитывая что задачи собственно никак не привязаны к потокам и могут выполнятся вообще без единого потока.
  3. По-моему, гораздо проще было всё объяснить просто обратив внимание на то, что создание задачи и выполнение задачи отделены друг от друга и могут бросать разные исключения. Достаточно вместо

Task.WaitAll(ComputeAsync(() =>
    {
        // do work
    }));

написать так
var task = ComputeAsync(() =>
    {
        // do work
    });
Task.WaitAll(task);

Сразу станет видно что тут ДВА места где могут возникнуть исключения.
По порядку:
  1. Слово недетерминированный является русским словом, принятым в научной сфере, а в математике — в частности. Учите мат. часть, как говорится.
    Если интересует этимология слова: конечно же, корень determine — определять.
    А слово фреймворк уже является неологизмом. остальные — на усмотрение автора.
    Спасибо, что хоть NB (но́та бэ́нэ) мне не запрещаете употреблять.
  2. ОК. Что такое таск, и что такое поток?
    Каждый из них является наименьшей единицей в моделях вытесняющей и кооперативной многозадачности. Так последнее эмулируется поверх первого, что очевидно.
    Кажется в статье, я четко указал на это:
    Концепция тасков весьма тесно связано с мыслью об асинхронности, которую иногда путают с многопоточным выполнением. А это в свою очередь приводит к выводу о том, что каждый вызов таска — нечто выполняющееся где-то там.
    Таск может выполнится в том же потоке, что и вызывающий код. Причем выполнение таска необязательно означает выполнение инструкций — это может быть просто Task.FromResult, например.

    Фраза о выполнении в каком-либо потоке ясно означает задачу (таск) с вычислениями.
  3. А что Хабр стал Твиттером? А немного поразмыслить? Все должно быть так просто?!
Я же не редактор, не надо принимать мои личные пожелания так близко к сердцу! Я описываю, как лично мне статья была бы более понятна.
Главное. Задачи (которые Task) в общем случае никак не связаны с потоками выполнения и многозадачностью (о чём вы сами кое-где упоминаете). Поэтому, для применения ваших правил независимо от ситуации, хотелось чтобы трудные ситуации описывались без привязки к потокам (которые Thread).
Ну и давайте улыбнёмся в тему.
Статья мне понравилась, продолжайте в том же духе! Было бы еще интересно ваши исследования в TPL Dataflow.
Sign up to leave a comment.

Articles