Pull to refresh

Comments 18

Rx смотрели? Очень похожий функционал.
Смотрел, но не использовал. Тоже событийный подход, но области применение немного разные, Rx больше нацелен на фильтрацию и обработку событий отдельным обработчиком, а Eventing на последовательное исполнение кода. При этом в Rx используются генераторы, которые могут приводить к блокировке потока
В Rx есть, например, FirstAsync() для последовательного кода. Про генераторы и блокировки не понял. Чего там действительно не хватает — так это ограничений скорости поступления событий из источника данных из-за отсутствия поддержки OnNextAsync и т.п.
UFO just landed and posted this here
Контекст синхронизации есть только у GUI-приложений, таких как WinForms или WPF,

Это неправда. встроенный контекст синхронизации есть в тех приложениях, где он оправдан (например, помимо перечисленных, он еще есть в asp.net). Но при этом можно создать и собственный контекст, если мы хотим получить какую-то его функциональность.


после оператора await поток создается другой

Это далеко не обязательно, и определяется конкретным используемым диспетчером.


Поэтому дабы избавиться от сложностей работы с этим контекстом

А там есть какие-то сложности? В большей части случаев он работает так, как и ожидается программистом. Поправки надо делать только в тех случаях, если вы нарушаете правило async all the way down.


Ну и да, вы всегда можете отказаться от возврата в контекст синхронизации.

UFO just landed and posted this here
Ну скажем так — asp.net тоже в некотором роде GUI

Нет, asp.net — это веб-фреймворк. И, скажем, когда на нем пишут WebAPI, никам UI там вообще не пахнет.


Вы можете привести конкретный пример когда при применении await не создается ожидающий callback поток?

В такой формулировке — никогда, вся прелесть TPL в том, что никто никаких коллбэков не ожидает, это continuation-passing. Но даже если предположить, что вы, на самом деле, спрашиваете про потоки, в которых выполняются continuations, то вот вам простейший пример:


private static async Task MainAsync()
        {
            WriteCurrentThread();
            await ImplAsync();
            WriteCurrentThread();
        }

        private static async Task ImplAsync()
        {
            WriteCurrentThread();
        }

Окей, пример надуманный (хотя, кстати, часто встречающийся в реальной жизни, именно поэтому в стейт-машине await под него есть оптимизация). Ладно, давайте его немного усложним:


        private static async Task MainAsync()
        {
            WriteCurrentThread();
            using (var r = new StreamReader(new FileStream("F:\\fp.mpcpl ", FileMode.Open, FileAccess.Read)))
            {
                await r.ReadToEndAsync();
            }
            WriteCurrentThread();
        }

Казалось бы, все, теперь мы всегда попадаем в другой поток… Но нет. Просто поменяем вызов c MainAsync().Wait() на AsyncContext.Run((Func<Task>) MainAsync) — и "волшебным" образом все continuations опять оказываются в одном потоке.


И вообще — как влияет диспетчера на работу await?

Извиняюсь, был не прав — не диспетчер, а шедулер.


Сложности работы с контекстом синхронизации состоят в том что его вообще приходиться использовать

Неа, в await это происходит прозрачно:


//some code accessing HttpContext.Current
await SomeInternalCode();
//some other code accessing HttpContext.Current

Вы явное использование контекста синхронизации видите? А тем не менее, контекст есть, и работает. Более того, если его убрать, у программиста просто не будет способа достать HttpContext.Current после await.

UFO just landed and posted this here
скажем так, это вопрос терминологии

Терминология — штука специально весьма конкретная.


а кто сказал что asp.net — это UI

Вы: "asp.net тоже в некотором роде GUI".


вот что я имел в виду — asp.net имеет пул, и в этом он сходен с UI — у которого есть очередь обрабатывающая события окна

Намешали. Что значит, "asp.net имеет пул"? Если вы о том, что каждый приходящий запрос попадает в поток из определенного пула, то это свойство хоста, а не asp.net. А уж сходства между очередью событий и пулом нет вовсе — очередь событий в Windows Forms/WPF однопоточна (собственно, она для того и сделана, чтобы не заниматься синхронизацией на интерфейсных элементах), а обработчик http-запросов в asp.net в хорошем случае может быть вообще неблокирующим (если потоков хватает и не используется сессия).


И именно поэтому он имеет тот же механизм унификации возврата из асинхронного вызова.

"Механизм унификации возврата" имеет TPL, и этот механизм называется SynchronizationContext. Вопрос только в том, какие хосты (и почему) по умолчанию запускают код в таком контексте, а какие — нет.


// здесь будет один поток ( скорее всего вызывающий)
WriteCurrentThread();
await ImplAsync();
// а вот здесь уже будет другой
WriteCurrentThread();

Эксперимент с вами не согласен:


static void Main(string[] args)
{
  MainAsync().Wait();
}

private static async Task MainAsync()
{
  WriteCurrentThread("MainAsync1");
  await ImplAsync();
  WriteCurrentThread("MainAsync2");
}

private static async Task ImplAsync()
{
  WriteCurrentThread("ImplAsync");
}

Вывод:


MainAsync1: 1
ImplAsync: 1
MainAsync2: 1

конечно есть, только тут используется ExecutionContext, внутри которого и будет искомый HttpContext.Current. Но это не есть использование контекста синхронизации

Правда? А почему же тогда в методе SetContinuationForAwait — а именно он в итоге отвечает за то, как будет вызван continuation после await — в первую очередь используется SynchronizationContext.CurrentNoFlow?


Здесь четко описана разница http://alz-it.blogspot.ru/2016/06/executioncontext-synchronizationcontext.html

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


When you await a task, by default the awaiter will capture the current SynchronizationContext, and if there was one, when the task completes it’ll Post the supplied continuation delegate back to that context, rather than running the delegate on whatever thread the task completed or rather than scheduling it to run on the ThreadPool.
UFO just landed and posted this here
это не единственный источник для моей статьи http://alz-it.blogspot.ru/2016/06/executioncontext-synchronizationcontext.html

Но один из основных (вплоть до дословного цитирования примеров кода).


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

Нет, не меняется. Когда awaiter размещает continuation, он смотрит на SynchronizationContext напрямую, минуя ExecutionContext. Более того, когда ExecutionContext передается через await, SynchronizationContext не захыватывается.


Поймите, ExecutionContext и SynchronizationContext в контексте (простите) await — ортогональны. ExecutionContext отвечает за то, какие данные будут видны коду после await, а SynchronizationContext — за то, где и когда код после await будет выполнен.


про точки прерывания мне даже неудобно как-то говорить…

А при чем тут "точки прерывания"?

Это не совсем так. Контекст синхронизации есть только у GUI-приложений

Ограничений на использование нет, в статье приводится пример с NUnit тестом. Если быть точным, выбор идет в таком порядке(MSDN):


  1. SynchronizationContext
  2. TaskScheduler
  3. ThreadPool
UFO just landed and posted this here
Именно это я и имел ввиду, что проверяется в том порядке, если null, то переходим к следующему. Конечно 4.5, ведь async с ним и добавили.
Вы можете получить тот же функционал, воспользовавшись акторной моделью: akka.net, orleans. В чем ваше преимущество?
Мне кажется некорректно сравнивать фреймворк и небольшую библиотеку.
но призывает немного по другому взглянуть на привычные вещи, позволяет писать безопасный и легко тестируемый с точки зрения многопоточности код.


Вы где-то кроме программирования State Machine пытались применить это решение? Насколько я понял вы отказались от использования асинхронного кода в связке с .NET ThreadPool — в пользу создания создания отдельного потока на каждый контекст и последовательного выполнения задач в нем. Для кастомизации и тюнинга таких низкоуровневых решений требуеться намного больше знания многопоточности, чем для обычной синхронизации кода с использованием async\await, и которые не требуют девелопера считать сколько и когда им создавать потоков на приложение\event bus\операцию.
UFO just landed and posted this here
Sign up to leave a comment.

Articles