В чем разница между структурами на основе push и pull, такими как IEnumerable <t> и IObservable <t>
В каждом разговоре по технологиям или в каждом сообщении в блоге, которое я читал о IEnumerable и IObservable, я читал, что IEnumerable - это структура, основанная на pull-based, и IObservable - это основанная на push структура.
Я читал, что с IObservable у нас есть асинхронные вызовы, где ничего не заблокировано, и все работает на основе push.
Но но НО...
Что это значит? На основе push и pull
Поскольку, по моему мнению, в IEnumerable мы можем также вставлять данные в структуру, а также извлекать из нее данные, я действительно потерял эти технические термины и идеи.
Пожалуйста, нормальным и человеческим способом объясните мне разницу между этими двумя структурами и разницей между структурами на основе push и pull.
Благодарю.
Ответы
Ответ 1
Пожалуйста, нормальным и человеческим способом объясните мне разницу
Хорошо, сделай тост. Это самая нормальная человеческая вещь, которую я делаю.
Основание:
// I want some toast. I will pull it out of the toaster when it is done.
// I will do nothing until the toast is available.
Toast t = toaster.MakeToast();
t.AddJam();
// Yum.
Нажатие:
// I want some toast. But it might take a while and I can do more work
// while I am waiting:
Task<Toast> task = toaster.MakeToastAsync();
Toast t = await task;
// await returns to the caller if the toast is not ready, and assigns
// a callback. When the toast is ready, the callback causes this method
// to start again from this point:
t.AddJam();
// Yum.
Вы хотите получить результат, вы вызываете функцию, и вы ничего не делаете до тех пор, пока функция не вернется.
Вы хотите, чтобы результат был нажат на вас, вы вызываете функцию async и ждете результата. Когда результат будет доступен, он будет нажат на обратный вызов, который возобновит метод, в котором он должен быть.
IEnumerable<T>
- это просто последовательность тяг; вы вызываете MoveNext
каждый раз, когда хотите вывести результат, и получите T
IObservable<T>
- это просто последовательность IObservable<T>
; вы регистрируете обратный вызов, и он вызывается каждый раз, когда доступен новый T
Иными словами: IEnumerable<T>
логически является последовательностью вызовов Func<T>
. IObservable<T>
логически является последовательностью продолжений Task<T>
. Не позволяйте тому факту, что они представляют собой последовательности, путают вас; это случайный. Основная идея состоит в том, что функции синхронны; вы называете их и получаете результат синхронно; если это займет некоторое время, вы ждете. Задачи асинхронны; вы запускаете их и получаете результат асинхронно, когда он доступен.
Эта идея существовала в С#, прежде чем IObservable
и await
. Другой способ взглянуть на это: pull-based похож на вызовы функций, push-based подобен обработчикам событий. Обычный вызов функции, вы называете это, когда хотите что-то. Обработчик событий, он вызывает вас, когда что-то происходит. События - это то, как сам язык С# представляет шаблон наблюдателя. Но события всегда логически формируют последовательность, поэтому имеет смысл манипулировать последовательностью нажатых элементов так же, как мы будем манипулировать последовательностью вытащенных элементов. И, следовательно, был изобретен IObservable
.
Ответ 2
Предполагая (логический) сервер и клиент, который определяет, когда данные доставляются, сервер или клиент?
Pull-based описывает схему клиентского запуска: клиенты делают запросы, и данные немедленно подаются. Push-based описывает схему запуска сервера: клиенты могут подключаться к потоку-потоку (IObservable), но они не могут требовать данных, они получают его, когда сервер чувствует, что дает ему.
Канонической формой pull будет запрос базы данных: вы отправляете запрос на сервер, а сервер отвечает набором элементов. Каноническая версия push будет чат-приложением: клиент чата не может "требовать" новых данных о разговоре, сервер скажет вам, когда другой человек что-то сказал.
Ответ 3
В дополнение к вышеупомянутым замечательным ответам я бы также предложил следующее:
- "IEnumerable", означающий "enumerable", неявно смешивается с "pull based" в концептуальной модели платформы.NET. Он предназначен для обозначения "позволяет вам получить счетчик, который позволяет вытащить следующее значение"
- Но у нас также есть IAsyncEnumerable.
- Математически перечислимое просто означает "счетный", т.е. Счетный набор.
- Наблюдаемые также могут быть счетными, в том смысле, что наблюдаемый может представлять собой набор дискретных событий.
Именование путается и, на мой взгляд, плохо отражает концепции, которые они намереваются. Например, в мире Java IEnumerable является Iterable, что ближе к намерению в.NET.
Я думаю, что проще всего представить IEnumerable и LINQ-конструкции вокруг них: "Получите мне некоторые данные из какого-то источника и фильтруйте их/группируйте их так...", тогда как Observables можно рассматривать как входящие потоки, на которые вы можете реагировать. Я не думаю, что всегда полезно рассматривать наблюдаемые как продолжения.
Ответ 4
Человек входит в продуктовый магазин и спрашивает лавочника, есть ли у него яйца. "Да", - говорит лавочник. "Могу ли я взять с собой?" - спрашивает мужчина. И лавочник дает человеку несколько яиц. "У тебя есть больше?" - спрашивает мужчина. "Да", - говорит лавочник. "Могу ли я взять с собой?" - спрашивает мужчина. И лавочник дает человеку несколько яиц. "У тебя есть больше?" - спрашивает мужчина. "Нет", - говорит лавочник. Человек уходит.
Это основано на тяге. Мужчина держит "вытаскивание" яиц у лавочника до тех пор, пока их не осталось.
Человек входит в продуктовый магазин и просит лавочника, если он может достать яйца, и если да, то может ли он доставить то, когда он сможет их получить. "Да", - говорит лавочник. Человек уходит. Через несколько дней приходят яйца. Еще через несколько дней приходят некоторые яйца. Затем человек называет лавочника и просит, чтобы поставки прекратились. Больше яиц не поступает.
Это толчок. Мужчина не ждет, пока лавочник даст ему яйца. Человек делает что-то еще, и лавочник "подталкивает" яйца к человеку.