Вопрос

Когда лучше использовать Rx вместо TPL или эти две платформы ортогональны?

Насколько я понимаю, Rx в первую очередь предназначен для предоставления абстракции над событиями и обеспечения возможности композиции, но он также позволяет предоставлять абстракцию над асинхронными операциями.использование перегрузок Createxx и перегрузок Fromxxx и отмена путем удаления возвращенного IDisposable.

TPL также предоставляет абстракцию для операций с помощью задач и возможностей отмены.

Моя дилемма: когда что использовать и для каких сценариев?

Это было полезно?

Решение

Основная цель Rx — не предоставлять абстракцию событий.Это лишь один из ее результатов.Его основная цель — предоставить составную модель push-уведомлений для коллекций.

Реактивная структура (Rx) основана на IObservable<T> являющийся математическим двойником IEnumerable<T>.Поэтому вместо того, чтобы «вытягивать» элементы из коллекции, используя IEnumerable<T> мы можем «пересылать» нам объекты через IObservable<T>.

Конечно, когда мы на самом деле ищем наблюдаемые источники, такие вещи, как события и асинхронные операции, являются отличными кандидатами.

Реактивная структура, естественно, требует многопоточной модели, чтобы иметь возможность отслеживать источники наблюдаемых данных и управлять запросами и подписками.Для этого Rx активно использует TPL.

Таким образом, если вы используете Rx, вы неявно используете TPL.

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

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

Другие советы

Некоторые рекомендации, которым я предпочитаю следовать:

  • Имею ли я дело с данными, источником которых не являюсь?Данные, которые поступают, когда заблагорассудится?Потом РХ.
  • Я создаю вычисления и мне нужно управлять параллелизмом?Потом ТПЛ.
  • Могу ли я управлять несколькими результатами и мне нужно выбирать из них в зависимости от времени?Потом РХ.

Мне нравятся пункты Скотта В.Чтобы поставить еще несколько конкретных примеров на картах RX, очень хорошо, чтобы

  • потребляющие потоки
  • выполнение неблокирующей асинхронной работы, например веб-запросов.
  • потоковые события (либо события .net, такие как движение мыши, либо события типа сообщения служебной шины)
  • Совместное составление «потоков» событий
  • Операции в стиле Linq
  • Предоставление потоков данных из вашего общедоступного API

TPL, кажется, хорошо сопоставляется с

  • внутреннее распараллеливание работы
  • выполнение неблокирующей асинхронной работы, например веб-запросов
  • выполнение рабочих потоков и продолжений

В отношении IObservable (Rx) я заметил одну вещь: он становится всепроникающим.Попав в базу кода, он, несомненно, будет доступен через другие интерфейсы, и в конечном итоге появится во всем вашем приложении.Полагаю, поначалу это может напугать, но большая часть команды сейчас вполне довольна Rx и им нравится объем работы, который он нам экономит.

ИМХО, Rx будет доминирующей библиотекой над TPL, поскольку она уже поддерживается в .NET 3.5, 4.0, Silverlight 3, Silverlight 4 и Javascript.Это означает, что вам фактически нужно изучить один стиль, и он применим ко многим платформам.

РЕДАКТИРОВАТЬ:Я изменил свое мнение о том, что Rx доминирует над TPL.Они решают разные проблемы, поэтому их не следует сравнивать подобным образом.В .NET 4.5/C# 5.0 ключевые слова async/await еще больше привяжут нас к TPL (и это хорошо).Для глубокого обсуждения Rx, событий, TPL и т. д.проверить первая глава из моей онлайн-книги IntroToRx.com

Обновление, декабрь 2016 г.: Если у вас есть 30 минут, я рекомендую вам прочитать рассказ Джо Даффи из первых рук, а не мои предположения.Я думаю, что мой анализ верен, но если вы нашли этот вопрос, я настоятельно рекомендую вам просмотреть сообщение в блоге вместо этих ответов, потому что помимо TPL и Rx.NET он также освещает исследовательские проекты MS (Midori, Cosmos).

http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/


Я думаю, что MS допустила большую ошибку, исправив ошибки после выхода .NET 2.0.Они одновременно представили множество различных API-интерфейсов управления параллелизмом из разных частей компании.

  • Стивен Тауб настойчиво настаивал на том, чтобы потокобезопасные примитивы заменили Event (который начинался как Future<T> и превратился в Task<T>)
  • MS Research имела MIN-LINQ и Reactive Extensions (Rx).
  • Аппаратное/встроенное было робототехника (CCR)

Тем временем многие команды управляемых API пытались жить с APM и Threadpool.QueueUserWorkItem(), не зная, выиграет ли Тоуб битву за корабль Future<T>/Task<T> в mscorlib.dll.В конце концов, похоже, они подстраховались и отправили оба Task<T> и IObservable<T> в mscorlib, но не допускал никаких других API Rx (даже ISubject<T>) в mscorlib.Я думаю, что это хеджирование в конечном итоге привело к огромному количеству дублирования (подробнее позже) и напрасной трате усилий внутри и за пределами компании.

О дублировании см.: Task против. IObservable<Unit>, Task<T> против. AsyncSubject<T>, Task.Run() против. Observable.Start().И это только верхушка айсберга.Но на более высоком уровне рассмотрим:

  • StreamInsight — потоки событий SQL, оптимизированные для собственного кода, но запросы событий определяются с использованием синтаксиса LINQ.
  • TPL Dataflow — построен на основе TPL, построен параллельно с Rx, оптимизирован для настройки параллелизма потоков, не очень хорош в составлении запросов.
  • Rx – Удивительная выразительность, но таящая в себе опасность.Смешивает «горячие» потоки с IEnumerable-методы расширения, что означает, что вы очень легко заблокируете навсегда (вызов First() на горячем потоке никогда не возвращается).Ограничения планирования (ограничение параллелизма) выполняются довольно странным способом. SubscribeOn() методы расширения, которые странно неявны и их трудно понять правильно.Если вы начинаете изучать Rx, выделите много времени, чтобы изучить все ловушки, которых следует избегать.Но Rx действительно единственный вариант, если вы составляете сложные потоки событий или вам нужна сложная фильтрация/запрос.

Я не думаю, что у Rx есть шансы на широкое распространение, пока MS не выйдет. ISubject<T> в мскорлибе.Это печально, потому что Rx содержит несколько очень полезных конкретных (обобщенных) типов, например TimeInterval<T> и Timestamped<T>, который, я думаю, должен быть в Core/mscorlib, например Nullable<T>.Также, System.Reactive.EventPattern<TEventArgs>.

Я бы сказал, что TPL Dataflow охватывает специализированное подмножество функций Rx.Поток данных предназначен для обработки данных, которая может занять измеримое количество времени, тогда как Rx предназначен для таких событий, как положение мыши, состояния ошибок и т. д., где время обработки незначительно.

Пример:ваш обработчик «подписки» является асинхронным, и вам нужно не более 1 исполнителя одновременно.С Rx вам придется блокировать, другого пути нет, потому что Rx не зависит от асинхронности и во многих местах не угрожает асинхронности особым образом.

.Subscribe(myAsyncHandler().Result)

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

Вы можете подумать, что если вы это сделаете

.ObserveOn(Scheduler.EventLoopSchedule)

чем проблема решена.Но это нарушит ваш рабочий процесс .Complete(), поскольку Rx подумает, что это выполнено, как только запланирует выполнение, и вы закроете свое приложение, не дожидаясь завершения асинхронной операции.

Если вы хотите разрешить не более 4 одновременных асинхронных задач, Rx не предлагает ничего «из коробки».Возможно, вы сможете что-то взломать, реализовав собственный планировщик, буфер и т. д.

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

Еще одна особенность TPL — «противодавление».Допустим, вы обнаружили ошибку в своей процедуре обработки и вам необходимо пересчитать данные за прошлый месяц.Если вы подписываетесь на свой источник с помощью Rx, и ваш конвейер содержит неограниченные буферы или ObserveOn, то у вас закончится память в считанные секунды, потому что источник будет продолжать читать быстрее, чем может обработать.Даже если вы реализуете блокировку потребителя, ваш источник может пострадать от блокировки вызовов, например, если источник является асинхронным.В TPL вы можете реализовать источник как

while(...)
    await actionBlock.SendAsync(msg)

который не блокирует источник, но будет ждать, пока обработчик перегружен.

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

Хорошая новость заключается в том, что блоки TPL Dataflow очень хорошо работают с Rx.У них есть адаптеры AsObserver/AsObservable, и при необходимости вы можете вставить их в середину конвейера Rx.Но у Rx гораздо больше шаблонов и вариантов использования.Поэтому мое практическое правило — начать с Rx и добавлять поток данных TPL по мере необходимости.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top