Недавно пришлось столкнуться с написанием алгоритма в условиях асинхронности появления входных данных.
Задача заключалось в написании на C# простенького торгового робота, входные данные — биржевые тики, изменения статусов заявок и т.п. —
поступали в мои подписчики на соответствующие события библиотеки, поставляющей данные.
Честно говоря, оказалось чертовски сложно перекладывать простейшую логику алгоритма на подобную асинхронность.
Некоторые с виду атомарные шаги алгоритма растягивались на появление нескольких событий.
Пришлось городить нечто вроде огромного конечного автомата.
Кстати еще было нужно мочь сериализовывать текущее состояние обработки и потом его восстанавливать.
Не поделитесь способами моделирования и технологиями реализации подобных задач?
У меня были мысли про цепочки коллбеков, вспомнился Twisted Framework..
Но реализовывать нечто подобное на базе C# event'ов и делегатов я пока не решился.
Re: Good practice по алгоритмизации в условиях асинхронности
Здравствуйте, sergunok, Вы писали:
S>Недавно пришлось столкнуться с написанием алгоритма в условиях асинхронности появления входных данных.
S>Задача заключалось в написании на C# простенького торгового робота, входные данные — биржевые тики, изменения статусов заявок и т.п. — S>поступали в мои подписчики на соответствующие события библиотеки, поставляющей данные.
S>Честно говоря, оказалось чертовски сложно перекладывать простейшую логику алгоритма на подобную асинхронность. S>Некоторые с виду атомарные шаги алгоритма растягивались на появление нескольких событий. S>Пришлось городить нечто вроде огромного конечного автомата. S>Кстати еще было нужно мочь сериализовывать текущее состояние обработки и потом его восстанавливать.
S>Не поделитесь способами моделирования и технологиями реализации подобных задач?
S>У меня были мысли про цепочки коллбеков, вспомнился Twisted Framework..
S>Но реализовывать нечто подобное на базе C# event'ов и делегатов я пока не решился.
Здравствуйте, gandjustas, Вы писали:
G>Смотри Rx
Давно интересовался практическими применениями этой либы, может есть у кого случаи успешного применения в рабочих проектах?
Re[3]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Aviator, Вы писали:
A>Здравствуйте, gandjustas, Вы писали:
G>>Смотри Rx A>Давно интересовался практическими применениями этой либы, может есть у кого случаи успешного применения в рабочих проектах?
У меня есть пример успешного применения в нерабочем проекте Переписал несложный сервер на Rx, код уменьшился раз 5, но в продакшн не пошло.
Здравствуйте, sergunok, Вы писали:
S>Не поделитесь способами моделирования и технологиями реализации подобных задач? S>Но реализовывать нечто подобное на базе C# event'ов и делегатов я пока не решился.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, Aviator, Вы писали:
A>>Здравствуйте, gandjustas, Вы писали:
G>>>Смотри Rx A>>Давно интересовался практическими применениями этой либы, может есть у кого случаи успешного применения в рабочих проектах?
G>У меня есть пример успешного применения в нерабочем проекте Переписал несложный сервер на Rx, код уменьшился раз 5, но в продакшн не пошло.
Интересно бы посмотреть за счёт чего такое уменьшение кода. Может просто рефакторинг?
Re[5]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Aviator, Вы писали:
A>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, Aviator, Вы писали:
A>>>Здравствуйте, gandjustas, Вы писали:
G>>>>Смотри Rx A>>>Давно интересовался практическими применениями этой либы, может есть у кого случаи успешного применения в рабочих проектах?
G>>У меня есть пример успешного применения в нерабочем проекте Переписал несложный сервер на Rx, код уменьшился раз 5, но в продакшн не пошло.
A>Интересно бы посмотреть за счёт чего такое уменьшение кода. Может просто рефакторинг?
За счет монад, неявного протаскивания контекста, наличия замыканий и массового использования лямбд.
Re: Good practice по алгоритмизации в условиях асинхронности
Здравствуйте, sergunok, Вы писали:
.... S>Пришлось городить нечто вроде огромного конечного автомата. S>Кстати еще было нужно мочь сериализовывать текущее состояние обработки и потом его восстанавливать.
S>Не поделитесь способами моделирования и технологиями реализации подобных задач?
WWF
Re[2]: Good practice по алгоритмизации в условиях асинхронно
Классная штука. Многие вопросы решит. И 100% пригодится.
Кстати сами применяли эту штуку для какой-нибудь подобной обработки событийных данных?
Кроме Lab'ов есть достойные материалы?
Очень интересно как в ней моделировать действия, состояющие из нескольких событий (эдакие длинные транзакции) и описывать их псевдопараллельную
обработку.
G>Смотри Rx
Re[3]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>Большое спасибо!!!!
S>Классная штука. Многие вопросы решит. И 100% пригодится.
S>Кстати сами применяли эту штуку для какой-нибудь подобной обработки событийных данных? S>Кроме Lab'ов есть достойные материалы?
Лучше всего видео на их сайте смотреть, там стараются толково объяснить базовые вещи, а все остальное легко делается после этого.
S>Очень интересно как в ней моделировать действия, состояющие из нескольких событий (эдакие длинные транзакции)
action1.SelectMany(action2) или from a in action1 from b in action2
S>и описывать их псевдопараллельную обработку.
.Join, .Amb итд
Re[6]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, gandjustas, Вы писали: A>>Интересно бы посмотреть за счёт чего такое уменьшение кода. Может просто рефакторинг? G>За счет монад, неявного протаскивания контекста, наличия замыканий и массового использования лямбд.
Т.е. код стал сильно похож на функциональный? А читаемость кода и удобство дебага не слишком сильно пострадало?
Re[6]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, gandjustas, Вы писали:
A>>Интересно бы посмотреть за счёт чего такое уменьшение кода. Может просто рефакторинг?
G>За счет монад, неявного протаскивания контекста, наличия замыканий и массового использования лямбд.
Несколько примерчиков можно кинуть?
Re[7]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Aviator, Вы писали:
A>Здравствуйте, gandjustas, Вы писали: A>>>Интересно бы посмотреть за счёт чего такое уменьшение кода. Может просто рефакторинг? G>>За счет монад, неявного протаскивания контекста, наличия замыканий и массового использования лямбд.
A>Т.е. код стал сильно похож на функциональный?
Ну да
A>А читаемость кода и удобство дебага не слишком сильно пострадало?
Ну учитывая что код был покрыт тестами и отлажен до переписывания, то не пострадало вообще.
Что касается писания на Rx с самого начала, то удобство отладки и тестирования только увеличивается, так IObservable очень легко мокать, в отличие от реализации state machine на коллбеках, а в самом Rx реализовано очень много комбинаторов, которые заменяют тучу "водопроводного" кода.
Re[7]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Aviator, Вы писали:
A>Здравствуйте, gandjustas, Вы писали:
A>>>Интересно бы посмотреть за счёт чего такое уменьшение кода. Может просто рефакторинг?
G>>За счет монад, неявного протаскивания контекста, наличия замыканий и массового использования лямбд.
A>Несколько примерчиков можно кинуть?
Здравствуйте, sergunok, Вы писали:
S>Честно говоря, оказалось чертовски сложно перекладывать простейшую логику алгоритма на подобную асинхронность. S>Некоторые с виду атомарные шаги алгоритма растягивались на появление нескольких событий. S>Пришлось городить нечто вроде огромного конечного автомата. S>Кстати еще было нужно мочь сериализовывать текущее состояние обработки и потом его восстанавливать.
Я все так пишу, мне привычно
Но вы можете выделить логику в отдельный поток. Соответственно, callback'и кладут куда-нибудь данные, будят этот поток и ждут ответа. Поток крутится в цикле ожидания запросов, получив запрос, он его обрабатывает, кладет куда-нибудь ответ, будит соответствуюший callback и засыпает в ожидании нового запроса.
Re[2]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Pzz, Вы писали:
Pzz>Но вы можете выделить логику в отдельный поток. Соответственно, callback'и кладут куда-нибудь данные, будят этот поток и ждут ответа. Поток крутится в цикле ожидания запросов, получив запрос, он его обрабатывает, кладет куда-нибудь ответ, будит соответствуюший callback и засыпает в ожидании нового запроса.
А смысл, если есть отличное решение в виде Rx? Если Rx чем-то не устраивает, то можете здесь написать.
Re[3]: Good practice по алгоритмизации в условиях асинхронно
Алгоритм к примеру такой (утопичный, но простой).
Всегда держать свою заявку на рынке с ценой на 1% выше рынка.
Таким образом в событие тика необходимо проверять какова цена текущей заявки, если она отличается от того, что нужно, инициировать снятие, при этом реально может придти событие как снятия так и исполнения (причем полного или неполного) и уже после успешного снятия выставить свою заявку..
Пhичем если процесс снятия уже инициирован, новый тик не должен бомбить биржу повторными снятиями.
У меня в голове сразу возникает понятие переменной с состояниями типа:
— заявка выставлена
— жду снятия
И куча if'ов в трех коллбеках, в привязке к состоянию.
Мысль про цепочки коллбеков у меня возникала (уже написал, навеяно питоновским twisted) и их поднавешивания в run-time.
А вот как тут поможет RX?
Я создаю три Observable коллекции, привязываю их к моим событиям (кстати, насколько понял RX требует наличия аргумента наследника от System.EventArgs). Дальше подписываюсь на эти коллекции и собственно говоря снова возникают те же самые обработчики и переменная состояния, но уже в контексте подписки..
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, Pzz, Вы писали:
Pzz>>Но вы можете выделить логику в отдельный поток. Соответственно, callback'и кладут куда-нибудь данные, будят этот поток и ждут ответа. Поток крутится в цикле ожидания запросов, получив запрос, он его обрабатывает, кладет куда-нибудь ответ, будит соответствуюший callback и засыпает в ожидании нового запроса.
G>А смысл, если есть отличное решение в виде Rx? Если Rx чем-то не устраивает, то можете здесь написать.
Re[4]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>Кстати, а можно сериализовать вместе с полями объекта установленные на данный момент обработчики да еще и контекст лямбда-выражений?
Конечно нет.
S>Сериализация, останов, десериализация также нужны для данной задачи.
Упаси бог, а зачем?
Re[4]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, sergunok, Вы писали:
S>>Кстати, а можно сериализовать вместе с полями объекта установленные на данный момент обработчики да еще и контекст лямбда-выражений? G>Конечно нет.
S>>Сериализация, останов, десериализация также нужны для данной задачи. G>Упаси бог, а зачем?
Для возможности продолжения работы алгоритма с последней точки сохранения.
Re[7]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, sergunok, Вы писали:
S>>>Кстати, а можно сериализовать вместе с полями объекта установленные на данный момент обработчики да еще и контекст лямбда-выражений? G>>Конечно нет.
S>>>Сериализация, останов, десериализация также нужны для данной задачи. G>>Упаси бог, а зачем?
S>Для возможности продолжения работы алгоритма с последней точки сохранения.
Это при наличии большого состояния, а в алгоритме обработки потоковой вряд ли такое будет.
Re[3]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>Очень интересно как в ней моделировать действия, состояющие из нескольких событий (эдакие длинные транзакции) и описывать их псевдопараллельную обработку.
может вам просто green threads нужны?
Люди, я люблю вас! Будьте бдительны!!!
Re[4]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, sergunok, Вы писали:
S>>Очень интересно как в ней моделировать действия, состояющие из нескольких событий (эдакие длинные транзакции) и описывать их псевдопараллельную обработку.
BZ>может вам просто green threads нужны?
Дык где же их в C# взять?
Re[4]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>У меня в голове сразу возникает понятие переменной с состояниями типа: S>- заявка выставлена S>- жду снятия S>И куча if'ов в трех коллбеках, в привязке к состоянию.
Это называется автомат В паттернах этому соответствует State.
S>А вот как тут поможет RX?
S>Я создаю три Observable коллекции, привязываю их к моим событиям (кстати, насколько понял RX требует наличия аргумента наследника от System.EventArgs). Дальше подписываюсь на эти коллекции и собственно говоря снова возникают те же самые обработчики и переменная состояния, но уже в контексте подписки..
Здесь будет тот же автомат, кстати говоря, только кода порожнего меньше.
Re: Good practice по алгоритмизации в условиях асинхронности
Здравствуйте, sergunok, Вы писали:
S>Некоторые с виду атомарные шаги алгоритма растягивались на появление нескольких событий. S>Пришлось городить нечто вроде огромного конечного автомата.
Почему нечто вроде ? Это и есть автомат.
S>Кстати еще было нужно мочь сериализовывать текущее состояние обработки и потом его восстанавливать.
А какие с этим проблемы если ты автомат уже выделил ?
Re[8]: Good practice по алгоритмизации в условиях асинхронно
S>>>>Сериализация, останов, десериализация также нужны для данной задачи. G>>>Упаси бог, а зачем?
S>>Для возможности продолжения работы алгоритма с последней точки сохранения.
G>Это при наличии большого состояния, а в алгоритме обработки потоковой вряд ли такое будет.
Видимо это нужно для долгоживущих процессов обработки, когда необходимо сохранять состояние между перезапусками программы.
Жизненный пример — интеграционные сообщения из шины MSMQ.
Re[9]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, achmed, Вы писали:
S>>>>>Сериализация, останов, десериализация также нужны для данной задачи. G>>>>Упаси бог, а зачем?
S>>>Для возможности продолжения работы алгоритма с последней точки сохранения.
G>>Это при наличии большого состояния, а в алгоритме обработки потоковой вряд ли такое будет.
A>Видимо это нужно для долгоживущих процессов обработки, когда необходимо сохранять состояние между перезапусками программы. A>Жизненный пример — интеграционные сообщения из шины MSMQ.
Ну так собственно и надо отдать хранение состояния вовне программы, а алгоритм желательно писать так чтобы не оперировать внутренним состоянием, а получать его извне, как и другие сигналы.
Re[5]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, sergunok, Вы писали:
S>>У меня в голове сразу возникает понятие переменной с состояниями типа: S>>- заявка выставлена S>>- жду снятия S>>И куча if'ов в трех коллбеках, в привязке к состоянию.
I>Это называется автомат В паттернах этому соответствует State.
Только паттерн state никак в этом деле не поможет.
Re[10]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
A>>Видимо это нужно для долгоживущих процессов обработки, когда необходимо сохранять состояние между перезапусками программы. A>>Жизненный пример — интеграционные сообщения из шины MSMQ.
G>Ну так собственно и надо отдать хранение состояния вовне программы, а алгоритм желательно писать так чтобы не оперировать внутренним состоянием, а получать его извне, как и другие сигналы.
Rx может в этом помочь?
Re[11]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, achmed, Вы писали:
A>Здравствуйте, gandjustas, Вы писали:
A>>>Видимо это нужно для долгоживущих процессов обработки, когда необходимо сохранять состояние между перезапусками программы. A>>>Жизненный пример — интеграционные сообщения из шины MSMQ.
G>>Ну так собственно и надо отдать хранение состояния вовне программы, а алгоритм желательно писать так чтобы не оперировать внутренним состоянием, а получать его извне, как и другие сигналы.
A>Rx может в этом помочь?
Хранить состояние вовне — вряд ли. Обрабатывать как остальные сигналы — вполне.
Re[6]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, gandjustas, Вы писали:
S>>>У меня в голове сразу возникает понятие переменной с состояниями типа: S>>>- заявка выставлена S>>>- жду снятия S>>>И куча if'ов в трех коллбеках, в привязке к состоянию.
I>>Это называется автомат В паттернах этому соответствует State.
G>Только паттерн state никак в этом деле не поможет.
Почему ?
Re[7]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, gandjustas, Вы писали:
S>>>>У меня в голове сразу возникает понятие переменной с состояниями типа: S>>>>- заявка выставлена S>>>>- жду снятия S>>>>И куча if'ов в трех коллбеках, в привязке к состоянию.
I>>>Это называется автомат В паттернах этому соответствует State.
G>>Только паттерн state никак в этом деле не поможет.
I>Почему ?
Потому что он нарушает линейность кода. Посмотри во что компилируется yield в C# и попробуй тоже самое переписать с паттерном state.
Переписывание линейного кода с точками прерывания (типа yield) в асинхронный код, операция хоть и тяжелая, но механическая(поэтому компилятор легко осиливает такое). А вот внедрение паттерна state заставляет перекраивать дизайн.
Re[12]: Good practice по алгоритмизации в условиях асинхронн
G>>>Ну так собственно и надо отдать хранение состояния вовне программы, а алгоритм желательно писать так чтобы не оперировать внутренним состоянием, а получать его извне, как и другие сигналы.
A>>Rx может в этом помочь?
G>Хранить состояние вовне — вряд ли. Обрабатывать как остальные сигналы — вполне.
получается нет смысла использовать Rx там где требуется хранить состояние во вне, жаль
Re[13]: Good practice по алгоритмизации в условиях асинхронн
G>>>>Ну так собственно и надо отдать хранение состояния вовне программы, а алгоритм желательно писать так чтобы не оперировать внутренним состоянием, а получать его извне, как и другие сигналы.
A>>>Rx может в этом помочь?
G>>Хранить состояние вовне — вряд ли. Обрабатывать как остальные сигналы — вполне.
A>получается нет смысла использовать Rx там где требуется хранить состояние во вне, жаль
Наоборот, не смысла использовать Rx там где надо хранить состояние внутри.
Re[8]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, gandjustas, Вы писали:
G>Переписывание линейного кода с точками прерывания (типа yield) в асинхронный код, операция хоть и тяжелая, но механическая(поэтому компилятор легко осиливает такое). А вот внедрение паттерна state заставляет перекраивать дизайн.
Разумеется, автомат в любом виде нужно выделить в виде явной структуры.
С RX у меня получается ровно тоже самое, только кода меньше
Re[9]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, gandjustas, Вы писали:
G>>Переписывание линейного кода с точками прерывания (типа yield) в асинхронный код, операция хоть и тяжелая, но механическая(поэтому компилятор легко осиливает такое). А вот внедрение паттерна state заставляет перекраивать дизайн.
I>Разумеется, автомат в любом виде нужно выделить в виде явной структуры.
Да, но паттерн state предполагает другой подход к проектированию.
I>С RX у меня получается ровно тоже самое, только кода меньше
А пример можно?
Я например не вижу зачем автомат нужен. Состояние передается через возвращаемое значение T в IObservable<T>, а последовательность задается с помощью bind SelectMany, в том числе можно задавать реакцию на ошибки.
Re[5]: Good practice по алгоритмизации в условиях асинхронно
// Статус заявки на покупкуpublic enum OrderStatusEn
{
Matched, // Реализована
Cancelled // Снята
}
// Заявка на покупкуpublic class Order
{
// Цена public double Price;
// Объемpublic int Volume;
// Cтатусpublic OrderStatusEn Status;
}
// Интерфейс к торговой системеpublic interface ITrade
{
// Регистрация заявки на покупку.void RegisterOrder(Order order);
// Снятие заявки на покупку.void CancelOrder(Order order);
// АСИНХРОННАЯ ЧАСТЬ
// Изменение статуса заявкиevent Action<Order> OrderChanged;
// Новый биржевой "тик"event Action<double> NewTick;
}
public class Algorithm
{
// Состояние алгоритмаprivate enum StateEn
{
// ничего не делаем, ожидаем события
Idle,
// покупка
Buying,
// снятие заявки на покупку
CancellingBuy
}
// Состояние алгоритма.private StateEn state;
// Текущий минимум цены на рынке.private double min;
private ITrade trade;
// Лимит - объем выставляемых заявок.private int limit;
// текущая заявка на покупку.private Order order;
public Algorithm(ITrade trade, int limit)
{
this.order = null;
this.min = double.MaxValue;
this.state = StateEn.Idle;
this.trade = trade;
this.trade.OrderChanged += new Action<Order>(orderChanged);
this.trade.NewTick += new Action<double>(newTick);
this.limit = limit;
}
private void orderChanged(Order order)
{
switch (order.Status)
{
case OrderStatusEn.Matched:
if (this.state == StateEn.Buying)
this.state = StateEn.Idle;
else if (this.state == StateEn.CancellingBuy) // пытались снять, но заявка успела реализоваться
buying();
break;
case OrderStatusEn.Cancelled:
if (this.state == StateEn.CancellingBuy)
buying();
break;
}
}
private void buying()
{
this.order = new Order() { Price = this.min, Volume = this.limit };
this.trade.RegisterOrder(this.order);
this.state = StateEn.Buying;
}
private void cancellingBuying()
{
this.trade.CancelOrder(this.order);
this.state = StateEn.CancellingBuy;
}
private void newTick(double price)
{
if (price < this.min) // минимум изменился (появился)
{
this.min = price;
if (this.state == StateEn.Idle)
{
buying();
}
else if (this.state == StateEn.Buying)
{
cancellingBuying();
}
}
}
}
Комментарии по примеру. Имеется API к бирже ITrade,
в него входят две команды к бирже: регистрация / снятие заявки на покупку
и два события — изменение статуса заявки (зарегистрирована / снята) и получение биржевого "тика".
Algorithm реализует стратегию покупки в момент изменения минимума цены.
Код обрабатывает ситуации типа "если минимум проапдейтился, но сейчас есть активная заявка, значит снять ее и по окончании выставить по новой цене"
Буду признателен, если подскажите как нечто подобное реализуется с помощью Rx и (или) в псевдосинхронном варианте с помощью yield.
Также в принципе интересно как описывать при использовании RX логику обработки в случае наложения действий:
— прекращение текущего действия, начала нового
— постановка нового действия в очередь
— пропуск нового
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, sergunok, Вы писали:
S>>А вот как тут поможет RX?
G>Лучше приведи код в упрощенном варианте.
Re[2]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>Код обрабатывает ситуации типа "если минимум проапдейтился, но сейчас есть активная заявка, значит снять ее и по окончании выставить по новой цене" S>Буду признателен, если подскажите как нечто подобное реализуется с помощью Rx и (или) в псевдосинхронном варианте с помощью yield.
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, sergunok, Вы писали:
S>>Код обрабатывает ситуации типа "если минимум проапдейтился, но сейчас есть активная заявка, значит снять ее и по окончании выставить по новой цене" S>>Буду признателен, если подскажите как нечто подобное реализуется с помощью Rx и (или) в псевдосинхронном варианте с помощью yield.
U>На TaskPulling'е
Чето я не понял где здесь асинхронность? Как использовать такой метод?
U>Требуемую бизнес-логику я не совсем понял, но вроде в такой алгоритм можно встроить все что душе угодно.
На самом деле такой алгоритм в линейной форме не выражается. Там два потока входящих событий и один поток исходящих, кореллирующих между собой.
Re[8]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, gandjustas, Вы писали:
U>>Требуемую бизнес-логику я не совсем понял, но вроде в такой алгоритм можно встроить все что душе угодно. G>На самом деле такой алгоритм в линейной форме не выражается. Там два потока входящих событий и один поток исходящих, кореллирующих между собой.
В чем смысл мыслить в терминах событий? Естественно на событиях такие задачи решаются очень сложно. А просто они решаются в терминах синхронизации того состояния которое есть с тем, которое должно быть.
Re[6]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>Algorithm реализует стратегию покупки в момент изменения минимума цены. S>Код обрабатывает ситуации типа "если минимум проапдейтился, но сейчас есть активная заявка, значит снять ее и по окончании выставить по новой цене"
Повторил насколько хватило моих знаний Rx, а их, как выяснилось, крайне мало.
public class Algorithm : IDisposable
{
// Состояние алгоритмаprivate enum StateEn
{
// ничего не делаем, ожидаем события
Idle,
// покупка
Buying,
// снятие заявки на покупку
CancellingBuy
}
private class State
{
public State(Order order, StateEn state, double price)
{
Order = order;
StateEn = state;
Price = price;
}
public Order Order { get; private set; }
public StateEn StateEn { get; private set; }
public double Price { get; private set; }
}
private ITrade trade;
// Лимит - объем выставляемых заявок.private int limit;
IDisposable cookie;
public Algorithm(ITrade trade, int limit)
{
this.trade = trade;
this.limit = limit;
//Последовательность состояний
IConnectableObservable<State> states = null;
//Создаем ленивость вычисленийvar deferedStates = Observable.Defer(() => states);
//Используем Subject чтобы сделать из событий IObservablevar orderChanged = new Subject<Order>();
var ticks = new Subject<double>();
this.trade.OrderChanged += o => orderChanged.OnNext(o);
this.trade.NewTick += v => ticks.OnNext(v);
//Последовательность минимальных ценvar minPrices = ticks.Scan(double.MaxValue, (min, v) => min > v ? v : min).DistinctUntilChanged();
//Разделяем потоки событий чтобы легче было писатьvar orderMatched = orderChanged.Where(o => o.Status == OrderStatusEn.Matched);
var orderCancelled = orderChanged.Where(o => o.Status == OrderStatusEn.Cancelled);
var idleState = deferedStates.Where(s => s.StateEn == StateEn.Idle);
var buyState = deferedStates.Where(s => s.StateEn == StateEn.Buying);
var cancellingState = deferedStates.Where(s => s.StateEn == StateEn.CancellingBuy);
//Объединяем несколько результирующих потоков в один
states =
Observable.Join(
//Если новая минимальная цена и ожидаем события, то покупаем
minPrices.And(idleState).Then((p, _) => Buy(p)),
//Если новая минимальная цена и покупаем, то отменям
minPrices.And(buyState)
.Then((p, s) =>
{
this.trade.CancelOrder(s.Order);
return new State(null, StateEn.CancellingBuy, p);
}),
//Если статус заявки изменился на matched и покупаем, то переходим в ожидание
orderMatched.And(buyState).Then((o, s) => new State(null, StateEn.Idle, s.Price)),
//Если статус заявки изменился и отменяем, то покупаем по новой цене
orderChanged.And(cancellingState).Then((_, s) => Buy(s.Price))
).Publish(new State(null, StateEn.Idle, 0));//Создается асинхронность
cookie = states.Subscribe(s =>
{
//Получили забесплатно логирование в одном месте#if DEBUG
Console.WriteLine("{0} {1} {2}", DateTime.Now, s.StateEn, s.Price);
#endif
});
states.Connect();
}
private State Buy(double price)
{
var order = new Order() { Price = price, Volume = this.limit };
this.trade.RegisterOrder(order);
return new State(order, StateEn.Buying, price);
}
public void Dispose()
{
cookie.Dispose();
}
}
В местах, выделенных жирным можно подсовывать начальные значения в случае перезапуска.
По сути ничем не лучше вариантов с обработчиками событий, кроме того что вся логика записана в одном месте.
Re[9]: Good practice по алгоритмизации в условиях асинхронно
Undying и gandjustas, спасибо за примеры кода!
Перевариваю их сейчас..
По поводу: U>В чем смысл мыслить в терминах событий? Естественно на событиях такие задачи решаются очень сложно. А просто они решаются в терминах синхронизации того состояния которое есть с тем, которое должно быть.
Очередь выходных событий (не очень нравится в данном случае термин "очередь выходных событий", скорее назвал бы очередью действий) ИМХО — это хороший способ решать широкий круг подобных задач. (Хотя для моего примера — это возможно "из пушки по воробьям").
Думаю, что при использовании очереди здорово разрешать действиям из очереди подписываться на входные события.
Для моего примера действиями могут быть:
WaitingAction
BuyAction
CancelingBuyAction
У каждого Action есть свой метод Do.
Подписывание на события:
WaitingAсtion подписывается на событие новый min
BuyAction на удовлетворение заявки и новый min
CancelingBuyAction на удовлетворение заявки или снятие
(В данном примере действия по сути совпали с состояниями)
Обработчикам доступны любые методы по модификации очереди действий и функция старта обработки след."действия" из очереди.
Re[10]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
S>Здравствуйте, Ikemefula, Вы писали:
I>>А какие с этим проблемы если ты автомат уже выделил ?
S>Проблемы буду скоро когда, буду трансформировать более сложный алгоритм в автомат.
S>Согласен с gandjustas, что для перевода синхронного алгоритма а автоматный вид требуются усилия (пускай и автоматизируемые).
S>Пока только не совсем понимаю как все это решается RX'ом или вот даже просто yield, последнее в Питоновском Twisted Framework S>решал сам фреймворк:
S>@inlineCallback S>def login(self, login, pwd): S> success = yield loginRemotely(login, pwd) S> self.data = yield loadData(login)
IObservable<Data> Login(string login, string pwd)
{
return from _ in LoginRemotely(login, pwd) //LoginRemotely возвращает IObservable<ЧТОНИТЬ>from d in LoadData(login) //LoadData возвращает IObservable<Data>select d;
}
Re[10]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
S>Очередь выходных событий (не очень нравится в данном случае термин "очередь выходных событий", скорее назвал бы очередью действий) ИМХО — это хороший способ решать широкий круг подобных задач. (Хотя для моего примера — это возможно "из пушки по воробьям").
Я не совсем понимаю, зачем в таких задачах вообще строить логику на событиях. Как я понимаю задача у тебя примерно такая. Есть некий биржевой товар, у которого время от времени изменяется цена. При изменениях цены и выполнение определенных условий надо либо удалить старую заявку, либо поставить новую заявку, либо вначале удалить старую, а затем поставить новую заявку. Соответственно на каждый биржевой товар заводим Task, пока цена не меняется Task спит, как только поменялась, Task просыпает и проверяет условия — нужно ли удалять существующую заявку (если она есть) и нужно ли ставить новую заявку. Проблема может быть в том, что добавление и удаление заявок являются асинхронным операциями, но эту проблему TaskPulling позволяет легко решить.
Достоинства. Все изменения производятся из одного места и в предсказуемом порядке (к примеру, вначале производится удаление старой заявки, затем добавление новой), поэтому легко гарантировать, что в конкретный момент времени может существовать только одна заявка, не нужно думать о том, что произойдет если вначале придет событие на удаление, тут же событие на добавление и тут же снова на удаление и т.п. Соответственно код получается простым и надежным.
Re[11]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, sergunok, Вы писали:
S>>Очередь выходных событий (не очень нравится в данном случае термин "очередь выходных событий", скорее назвал бы очередью действий) ИМХО — это хороший способ решать широкий круг подобных задач. (Хотя для моего примера — это возможно "из пушки по воробьям").
U>Я не совсем понимаю, зачем в таких задачах вообще строить логику на событиях. Как я понимаю задача у тебя примерно такая. Есть некий биржевой товар, у которого время от времени изменяется цена. При изменениях цены и выполнение определенных условий надо либо удалить старую заявку, либо поставить новую заявку, либо вначале удалить старую, а затем поставить новую заявку. Соответственно на каждый биржевой товар заводим Task, пока цена не меняется Task спит, как только поменялась, Task просыпает и проверяет условия — нужно ли удалять существующую заявку (если она есть) и нужно ли ставить новую заявку. Проблема может быть в том, что добавление и удаление заявок являются асинхронным операциями, но эту проблему TaskPulling позволяет легко решить.
Честно говоря не понимаю терминов, которые ты используешь. Можешь пример рабочего кода привести и рассказать что такое Task и TaskPulling.
Если Task это System.Threading.Tasks.Task, то то что ты описываешь ничуть не лучше Rx и потоков событий (асинхронных коллекций), а даже хуже, так как потребует большого количества водопроводного кода.
Re[12]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Честно говоря не понимаю терминов, которые ты используешь.
Что именно непонятно? Сам принцип замены логики на событиях на логику синхронизации текущего состояния с требуемым? Или принцип понятен, но непонятно как реализуется синхронизация состояний при наличии асинхронных операций на TaskPulling'е?
G>Можешь пример рабочего кода привести и рассказать что такое Task и TaskPulling.
G>Если Task это System.Threading.Tasks.Task, то то что ты описываешь ничуть не лучше Rx и потоков событий (асинхронных коллекций), а даже хуже, так как потребует большого количества водопроводного кода.
Имеется в виду Task из TaskPulling'а.
Re[13]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>Честно говоря не понимаю терминов, которые ты используешь.
U>Что именно непонятно? Сам принцип замены логики на событиях на логику синхронизации текущего состояния с требуемым? Или принцип понятен, но непонятно как реализуется синхронизация состояний при наличии асинхронных операций на TaskPulling'е?
Непонятно как реализуется асинхронность в данном случае. Выполнение кода должно как-то прерываться и потом возвращаться назад.
G>>Можешь пример рабочего кода привести и рассказать что такое Task и TaskPulling. U>Описание здесь: http://rsdn.ru/forum/dotnet/3037674.1.aspx
Ну если все опирается на таймер, то так делать не стоит.
G>>Если Task это System.Threading.Tasks.Task, то то что ты описываешь ничуть не лучше Rx и потоков событий (асинхронных коллекций), а даже хуже, так как потребует большого количества водопроводного кода. U>Имеется в виду Task из TaskPulling'а.
Тогда уж лучше взять таски из TPL и ParallelExtensionExtras
Re[14]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
U>>Что именно непонятно? Сам принцип замены логики на событиях на логику синхронизации текущего состояния с требуемым? Или принцип понятен, но непонятно как реализуется синхронизация состояний при наличии асинхронных операций на TaskPulling'е? G>Непонятно как реализуется асинхронность в данном случае. Выполнение кода должно как-то прерываться и потом возвращаться назад.
Для этого и существуют yield'ы. Например, у нас изменилась цена товара, из-за чего старую заявку нужно удалить, удаление заявки операция асинхронная. Соответственно таска, заметив изменение цены, активируется и начинает удаление заявки, пока заявка не удалена таска ждет (с помощью yield return new WaitStep()). Пока заявка не будет удалена новые изменения цены будут либо игнорироваться, либо складываться в очередь, в зависимости от того, что требуется по условию задачи. Как только заявка удалилась таска продолжает свою работу.
G>>>Можешь пример рабочего кода привести и рассказать что такое Task и TaskPulling. U>>Описание здесь: http://rsdn.ru/forum/dotnet/3037674.1.aspx
G>Ну если все опирается на таймер, то так делать не стоит.
Таймер это решение для бедных, если псевдосинхронная запись решение задачи облегачает, а использовать TaskPulling по каким-то причинам не хочется. Собственно TaskPulling никакие таймеры не использует.
Re[15]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Что именно непонятно? Сам принцип замены логики на событиях на логику синхронизации текущего состояния с требуемым? Или принцип понятен, но непонятно как реализуется синхронизация состояний при наличии асинхронных операций на TaskPulling'е? G>>Непонятно как реализуется асинхронность в данном случае. Выполнение кода должно как-то прерываться и потом возвращаться назад.
U>Для этого и существуют yield'ы. Например, у нас изменилась цена товара, из-за чего старую заявку нужно удалить, удаление заявки операция асинхронная. Соответственно таска, заметив изменение цены, активируется и начинает удаление заявки, пока заявка не удалена таска ждет (с помощью yield return new WaitStep()). Пока заявка не будет удалена новые изменения цены будут либо игнорироваться, либо складываться в очередь, в зависимости от того, что требуется по условию задачи. Как только заявка удалилась таска продолжает свою работу.
Это я в курсе. Меня другое интересует. Вот есть асинхронный вызов, типа BeginRead, на нем я хочу прервать выполнение с помощью yield и вернутся когда вызов завершится. Значит мне надо что-то передать в callback этого самого BeginRead, что вызовет продолжение работы с места прерывания.
G>>>>Можешь пример рабочего кода привести и рассказать что такое Task и TaskPulling. U>>>Описание здесь: http://rsdn.ru/forum/dotnet/3037674.1.aspx
G>>Ну если все опирается на таймер, то так делать не стоит.
U>Таймер это решение для бедных, если псевдосинхронная запись решение задачи облегачает, а использовать TaskPulling по каким-то причинам не хочется. Собственно TaskPulling никакие таймеры не использует.
А что использует?
Кстати с помощью Rx итераторы делаются очень легко.
Пример (чтение заданного количества байт из потока):
public static IObservable<Unit> ReadExact(this Stream stream, byte[] buffer, int offset, int count)
{
return ReadExactInternal(stream, buffer, offset, count).Concat().TakeLast(1).Select(_ => new Unit());
}
public static IEnumerable<IObservable<int>> ReadExactInternal(Stream stream, byte[] buffer, int offset, int count)
{
var read = Observable.FromAsyncPattern<byte[], int, int, int>(stream.BeginRead, stream.EndRead);
while(count>0)
{
int bytesRead = 0;
yield return read(buffer, offset, count).Do(r => bytesRead = r);
count -= bytesRead;
offset += bytesRead;
}
}
Re[13]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Это я в курсе. Меня другое интересует. Вот есть асинхронный вызов, типа BeginRead, на нем я хочу прервать выполнение с помощью yield и вернутся когда вызов завершится. Значит мне надо что-то передать в callback этого самого BeginRead, что вызовет продолжение работы с места прерывания.
U>>Таймер это решение для бедных, если псевдосинхронная запись решение задачи облегачает, а использовать TaskPulling по каким-то причинам не хочется. Собственно TaskPulling никакие таймеры не использует. G>А что использует?
TaskPulling ставит таску в очередь указанного потока. Далее поток просыпается берет первую таску из очереди, делает очередной шаг таски, после чего таска сдвигается в конец очереди (реально посложнее немного, но не суть).
Re[14]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Посмотрел код, там потоки запускаются, не пойдет. G>Примерно в тоже время в MSDN Magazine появилась статья про AsyncEnumerator, который без дополнительных потоков работает.
Написать вариант TaskPulling'а базирующийся на WinForms.Timer и соответственно работающий в том же потоке дело 15 минут, только на большинстве задач смысла в этом никакого, исключение, наверное, некоторые задачи связанные с гуи.
Re[15]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>Посмотрел код, там потоки запускаются, не пойдет. G>>Примерно в тоже время в MSDN Magazine появилась статья про AsyncEnumerator, который без дополнительных потоков работает.
U>Написать вариант TaskPulling'а базирующийся на WinForms.Timer и соответственно работающий в том же потоке дело 15 минут, только на большинстве задач смысла в этом никакого, исключение, наверное, некоторые задачи связанные с гуи.
Таймер слишком редко срабатывает для таких задач, а плодить потоки — быстро придет белый пушной зверек под нагрузкой.
Нужно полностью асинхронный механизм работы (на коллбеках), в TPL и Rx такой есть, зачем что-то еще использовать?
Re[16]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Таймер слишком редко срабатывает для таких задач, а плодить потоки — быстро придет белый пушной зверек под нагрузкой.
Зачем плодить потоки? Кто мешает ставить произвольное количество тасок в один и тот же поток?
Re[16]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Нужно полностью асинхронный механизм работы (на коллбеках), в TPL и Rx такой есть, зачем что-то еще использовать?
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>Таймер слишком редко срабатывает для таких задач, а плодить потоки — быстро придет белый пушной зверек под нагрузкой.
U>Зачем плодить потоки? Кто мешает ставить произвольное количество тасок в один и тот же поток?
Да никто не мешает, только поток висит в ожидании после выполнения шага. При большом количестве задач в очереди такая схема начинает сосать со страшной силой, поэтому надо плодить потоки.
Re[18]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Да никто не мешает, только поток висит в ожидании после выполнения шага. При большом количестве задач в очереди такая схема начинает сосать со страшной силой, поэтому надо плодить потоки.
Если это проблема, то в TaskPulling надо добавить возможность форсированного опроса таски и затем дергать этот форсированный опрос из callback'а. Добавление займет минут пятнадцать.
Re[18]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Да никто не мешает, только поток висит в ожидании после выполнения шага. При большом количестве задач в очереди такая схема начинает сосать со страшной силой, поэтому надо плодить потоки.
Или ты о чем? Что ты понимаешь под "поток висит в ожидании после выполнения шага"?
Re[17]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>Нужно полностью асинхронный механизм работы (на коллбеках), в TPL и Rx такой есть, зачем что-то еще использовать?
U>Если на них можно решать задачи только в таком стиле: http://rsdn.ru/forum/design/3929847.1.aspx
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>Да никто не мешает, только поток висит в ожидании после выполнения шага. При большом количестве задач в очереди такая схема начинает сосать со страшной силой, поэтому надо плодить потоки.
U>Если это проблема, то в TaskPulling надо добавить возможность форсированного опроса таски и затем дергать этот форсированный опрос из callback'а. Добавление займет минут пятнадцать.
И переписывание всего кода с коллбеками два года
Re[19]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>Да никто не мешает, только поток висит в ожидании после выполнения шага. При большом количестве задач в очереди такая схема начинает сосать со страшной силой, поэтому надо плодить потоки.
U>Или ты о чем? Что ты понимаешь под "поток висит в ожидании после выполнения шага"?
Это значит что ни одно событие, произошедшее в этот момент, не будет обработано. Учитывая довольно низкий accuracy системного таймера, то даже при уменьшении времени ожидания будет некоторый порог пропускной способности.
Re[20]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
U>>Если это проблема, то в TaskPulling надо добавить возможность форсированного опроса таски и затем дергать этот форсированный опрос из callback'а. Добавление займет минут пятнадцать.
G>И переписывание всего кода с коллбеками два года
Зачем переписывать код, если он и так работает прекрасно?
Re[11]: Good practice по алгоритмизации в условиях асинхронн
G>Это значит что ни одно событие, произошедшее в этот момент, не будет обработано.
Я ж написал:
Пока заявка не будет удалена новые изменения цены будут либо игнорироваться, либо складываться в очередь, в зависимости от того, что требуется по условию задачи.
В чем проблема-то? Сложно очередь для событий создать? Неужто сложнее, чем разруливать все возможные последствия вызова событий в произвольном порядке?
Re[18]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, sergunok, Вы писали:
S>>Здравствуйте, Ikemefula, Вы писали:
I>>>А какие с этим проблемы если ты автомат уже выделил ?
S>>Проблемы буду скоро когда, буду трансформировать более сложный алгоритм в автомат.
S>>Согласен с gandjustas, что для перевода синхронного алгоритма а автоматный вид требуются усилия (пускай и автоматизируемые).
S>>Пока только не совсем понимаю как все это решается RX'ом или вот даже просто yield, последнее в Питоновском Twisted Framework S>>решал сам фреймворк:
S>>@inlineCallback S>>def login(self, login, pwd): S>> success = yield loginRemotely(login, pwd) S>> self.data = yield loadData(login)
G>
G>IObservable<Data> Login(string login, string pwd)
G>{
G> return from _ in LoginRemotely(login, pwd) //LoginRemotely возвращает IObservable<ЧТОНИТЬ>
G> from d in LoadData(login) //LoadData возвращает IObservable<Data>
G> select d;
G>}
G>
А можно два асинхронных действия разбить на два LINQ запроса?
В своем примере пропустил if:
Кстати, а вообще, касаемо Microsoft'овских рекомендаций, т.е. вот примерно такой способ (c RX и LINQ) MS и рекомендует для визуально синхронного описания по сути асинхронного кода?
Эх! сижу на дачном скайлинке и не могу видеотьюториалы RX полицезреть
Re[12]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Если это проблема, то в TaskPulling надо добавить возможность форсированного опроса таски и затем дергать этот форсированный опрос из callback'а. Добавление займет минут пятнадцать.
G>>И переписывание всего кода с коллбеками два года
U>Зачем переписывать код, если он и так работает прекрасно?
Ну это без нагрузки. Ты же рекомендуешь этот способ другим людям, а им может не хватить быстродействия.
Re[21]: Good practice по алгоритмизации в условиях асинхронн
G>>Это значит что ни одно событие, произошедшее в этот момент, не будет обработано.
U>Я ж написал:
U>
U>Пока заявка не будет удалена новые изменения цены будут либо игнорироваться, либо складываться в очередь, в зависимости от того, что требуется по условию задачи.
U>В чем проблема-то? Сложно очередь для событий создать? Неужто сложнее, чем разруливать все возможные последствия вызова событий в произвольном порядке?
Я не про конкретную задачу, в про общий подход. Это код кстати из LabeledThread.cs.
Приведи как будет выглядеть метод асинхронного чтения заданного числа байт из потока или асинхронное копирование потоков.
Re[19]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Если на них можно решать задачи только в таком стиле: http://rsdn.ru/forum/design/3929847.1.aspx
Здравствуйте, gandjustas, Вы писали:
U>>И какое отношение приведенный код имеет к той задаче, которую решает автор топика? G>Примерно такое же, как и то что ты написал.
Т.е. ты понимаешь задачу автора топика принципиально не так?
Как я понимаю задача у тебя примерно такая. Есть некий биржевой товар, у которого время от времени изменяется цена. При изменениях цены и выполнение определенных условий надо либо удалить старую заявку, либо поставить новую заявку, либо вначале удалить старую, а затем поставить новую заявку. Соответственно на каждый биржевой товар заводим Task, пока цена не меняется Task спит, как только поменялась, Task просыпает и проверяет условия — нужно ли удалять существующую заявку (если она есть) и нужно ли ставить новую заявку.
Потому как то, что я написал, к решению вот этой задачи имеет самое прямое отношение.
Re[5]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, sergunok, Вы писали:
S>>>Здравствуйте, Ikemefula, Вы писали:
I>>>>А какие с этим проблемы если ты автомат уже выделил ?
S>>>Проблемы буду скоро когда, буду трансформировать более сложный алгоритм в автомат.
S>>>Согласен с gandjustas, что для перевода синхронного алгоритма а автоматный вид требуются усилия (пускай и автоматизируемые).
S>>>Пока только не совсем понимаю как все это решается RX'ом или вот даже просто yield, последнее в Питоновском Twisted Framework S>>>решал сам фреймворк:
S>>>@inlineCallback S>>>def login(self, login, pwd): S>>> success = yield loginRemotely(login, pwd) S>>> self.data = yield loadData(login)
G>>
G>>IObservable<Data> Login(string login, string pwd)
G>>{
G>> return from _ in LoginRemotely(login, pwd) //LoginRemotely возвращает IObservable<ЧТОНИТЬ>
G>> from d in LoadData(login) //LoadData возвращает IObservable<Data>
G>> select d;
G>>}
G>>
S>А можно два асинхронных действия разбить на два LINQ запроса?
Можно, linq запрос возвращает IQueryable<T> в данном случае, который отлично композируется с другими запросами.
S>В своем примере пропустил if:
S>
IObservable<Data> Login(string login, string pwd)
{
return from sucess in LoginRemotely(login, pwd) //LoginRemotely возвращает IObservable<bool>where sucessfrom d in LoadData(login) //LoadData возвращает IObservable<Data>select d;
}
В случае удачного логина будет выполнен LoadData, иначе сразу же вызовется OnCompleted.
S>Кстати, а вообще, касаемо Microsoft'овских рекомендаций, т.е. вот примерно такой способ (c RX и LINQ) MS и рекомендует для визуально синхронного описания по сути асинхронного кода?
WF
В .NET FW нету другой библиотеки для этого. В, стандартном теперь, TPL нету такой возможности (но есть в extras), а Rx далек от релиза еще.
Хотя в F# есть отличный async (и MailboxProcessor) — рекомендуется (те присутствует в официальной документации) только он.
Re[21]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>И какое отношение приведенный код имеет к той задаче, которую решает автор топика? G>>Примерно такое же, как и то что ты написал.
U>Т.е. ты понимаешь задачу автора топика принципиально не так?
U>
U>Как я понимаю задача у тебя примерно такая. Есть некий биржевой товар, у которого время от времени изменяется цена. При изменениях цены и выполнение определенных условий надо либо удалить старую заявку, либо поставить новую заявку, либо вначале удалить старую, а затем поставить новую заявку. Соответственно на каждый биржевой товар заводим Task, пока цена не меняется Task спит, как только поменялась, Task просыпает и проверяет условия — нужно ли удалять существующую заявку (если она есть) и нужно ли ставить новую заявку.
Ну если строго определить последовательность событий и order одного типа может быть только один, то тогда задача вырождается в КА. Но реальные торговые системы (насколько я знаю) чуть посложнее и оперируют именно событиями на входе и действиями на выходе. Их тоже можно свести к КА, но состояний у него будет дофига и он точно не будет описываться линейным кодом.
U>Потому как то, что я написал, к решению вот этой задачи имеет самое прямое отношение.
Ну оно имеет отношение к тому как ты её понял
Я предлагаю тебе игру на твоем поле. Напиши асинхронное копирование потоков с помощью TaskPulling , а то я посмотрев код не могу понять как оно должно выглядеть.
Re[22]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Ну если строго определить последовательность событий и order одного типа может быть только один, то тогда задача вырождается в КА. Но реальные торговые системы (насколько я знаю) чуть посложнее и оперируют именно событиями на входе и действиями на выходе. Их тоже можно свести к КА, но состояний у него будет дофига и он точно не будет описываться линейным кодом.
Если автор топика опишет задачу более конкретно, тогда можно будет сказать к чему она сводится.
U>>Потому как то, что я написал, к решению вот этой задачи имеет самое прямое отношение. G>Ну оно имеет отношение к тому как ты её понял
Если ты понял по другому, то напиши какое поведение требуется в таком-то случае и почему такое поведение нельзя реализовать через синхронайзер.
G>Я предлагаю тебе игру на твоем поле. Напиши асинхронное копирование потоков с помощью TaskPulling , а то я посмотрев код не могу понять как оно должно выглядеть.
Что такое "асинхронное копирование потоков"?
Re[23]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>Ну если строго определить последовательность событий и order одного типа может быть только один, то тогда задача вырождается в КА. Но реальные торговые системы (насколько я знаю) чуть посложнее и оперируют именно событиями на входе и действиями на выходе. Их тоже можно свести к КА, но состояний у него будет дофига и он точно не будет описываться линейным кодом.
U>Если автор топика опишет задачу более конкретно, тогда можно будет сказать к чему она сводится.
Авто
U>>>Потому как то, что я написал, к решению вот этой задачи имеет самое прямое отношение. G>>Ну оно имеет отношение к тому как ты её понял
U>Если ты понял по другому, то напиши какое поведение требуется в таком-то случае и почему такое поведение нельзя реализовать через синхронайзер.
G>>Я предлагаю тебе игру на твоем поле. Напиши асинхронное копирование потоков с помощью TaskPulling , а то я посмотрев код не могу понять как оно должно выглядеть.
U>Что такое "асинхронное копирование потоков"?
public static void CopyTo(this Stream source, Stream dest, int bufferSize = 1024)
{
var buffer = new byte[bufferSize];
int bytesRead = 0;
while((bytesRead = source.Read(buffer,0,bufferSize)) > 0)
{
dest.Write(buffer,0,bytesRead);
}
}
Вот такой код, только в асинхронном варианте.
Re[22]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
U>>Зачем переписывать код, если он и так работает прекрасно? G>Ну это без нагрузки. Ты же рекомендуешь этот способ другим людям, а им может не хватить быстродействия.
Во-первых, задача автора топика явно не тот случай. Во-вторых, еще раз повторяю, добавление метода ForceTask позволяющего совмещать пробуждение потока по времени с пробуждением по событию никаких принципиальных переделок не требуется.
Re[24]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
U>>Если автор топика опишет задачу более конкретно, тогда можно будет сказать к чему она сводится.
G>Авто
Ты здесь недописал что-то?
U>>Если ты понял по другому, то напиши какое поведение требуется в таком-то случае и почему такое поведение нельзя реализовать через синхронайзер.
Re[23]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Зачем переписывать код, если он и так работает прекрасно? G>>Ну это без нагрузки. Ты же рекомендуешь этот способ другим людям, а им может не хватить быстродействия.
U>Во-первых, задача автора топика явно не тот случай.
Явно тот, автотрейдинг работает под охренительными нагрузками.
U>Во-вторых, еще раз повторяю, добавление метода ForceTask позволяющего совмещать пробуждение потока по времени с пробуждением по событию никаких принципиальных переделок не требуется.
Чего? Движка или клиентского кода?
Re[25]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
U>>Во-первых, задача автора топика явно не тот случай. G>Явно тот, автотрейдинг работает под охренительными нагрузками.
Цены на сколько видов товаров надо отслеживать? Как часто изменяется цена на товар? Сколько занимает постановка и удаление заявки?
U>>Во-вторых, еще раз повторяю, добавление метода ForceTask позволяющего совмещать пробуждение потока по времени с пробуждением по событию никаких принципиальных переделок не требуется. G>Чего? Движка или клиентского кода?
В общем-то ни того, ни того. В движок надо добавить флажок IsForce в TaskWithTicks, метод ForceTask(Task task) в LabeledThread, метод ForceTask(Task task, string threadLabel) в TaskPull, проверку на IsCompleted перед вызовом taskWithTicks.Task.Execute в ThreadHandler и проверку на IsForce перед InsertTask там же. Работа на полчаса.
В клиентском коде нужно добавить в обработчики событий вызовы метода ForceTask, полагаю тоже в полчаса уложиться можно.
Re[26]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>В общем-то ни того, ни того. В движок надо добавить флажок IsForce в TaskWithTicks, метод ForceTask(Task task) в LabeledThread, метод ForceTask(Task task, string threadLabel) в TaskPull, проверку на IsCompleted перед вызовом taskWithTicks.Task.Execute в ThreadHandler и проверку на IsForce перед InsertTask там же. Работа на полчаса.
Хотя нет, я не прав, проблема, что у меня внутри асинхронного метода не будет ссылки на Task. Т.е. если пошаманить работать будет, а вот красивого решения пока что не вижу.
Re[25]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Во-первых, задача автора топика явно не тот случай. G>>Явно тот, автотрейдинг работает под охренительными нагрузками.
U>Цены на сколько видов товаров надо отслеживать?
Самые тупые стратегии работают с одним, продвинутые — гораздо больше.
U>Как часто изменяется цена на товар?
Несколько раз в секунду — спокойно.
U>Сколько занимает постановка и удаление заявки?
Самая быстра покупка после выставления заявки была за 0.15 сек.
Иногда раунд-трип до сервера больше
U>>>Во-вторых, еще раз повторяю, добавление метода ForceTask позволяющего совмещать пробуждение потока по времени с пробуждением по событию никаких принципиальных переделок не требуется. G>>Чего? Движка или клиентского кода?
U>В общем-то ни того, ни того. В движок надо добавить флажок IsForce в TaskWithTicks, метод ForceTask(Task task) в LabeledThread, метод ForceTask(Task task, string threadLabel) в TaskPull, проверку на IsCompleted перед вызовом taskWithTicks.Task.Execute в ThreadHandler и проверку на IsForce перед InsertTask там же. Работа на полчаса.
U>В клиентском коде нужно добавить в обработчики событий вызовы метода ForceTask, полагаю тоже в полчаса уложиться можно.
Ну вот уже больше похоже на async enumerator
Вопрос номер два. Как дождаться двух событий?
Re[27]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>А смысл? Read и Write выполняются синхронно.
U>Чтения блока естественно выполняется синхронно, а как оно еще может выполняться?
Асинхронно.
G>>Надо использовать BeginRead\EndRead, BeginWrite\EndWrite. U>Зачем? В чем будет преимущество?
В том что поток на время чтения не будет блокироваться.
U>Или тебе просто нужен пример кода, в котором об окончании операции оповещает callback? Тогда я вроде приводил: http://rsdn.ru/forum/design/3929522.1.aspx
G>Ну если строго определить последовательность событий и order одного типа может быть только один, то тогда задача вырождается в КА. Но реальные торговые системы (насколько я знаю) чуть посложнее и оперируют именно событиями на входе и действиями на выходе. Их тоже можно свести к КА, но состояний у него будет дофига и он точно не будет описываться линейным кодом.
Присоединяюсь к утверждению.
Мой пример всего лишь упрощение, чтобы не постить сюда горы кода.
В реальных стратегиях все сложнее и хочется освоить модель их описания и технологию реализации этой модели (в данном случае в .NET).
По поводу многопоточности, пока такой потребности нет. На данном этапе хочу работать с одной ниткой. Но в будущем скорее всего понадобится.
По поводу скоростей — действительно это запросто десятки событий в секунду.
Текущий итог по подпроблемам и возможным решениям:
1. Модель обработки.
Пока мне ближе всего модель "входные события" + "очередь реакций".
Причем реакция может быть "сложной", до своего завершения требовать прихода нескольких различных событий и отсылки ответных воздействий. Пример — реализация большого объема акций. На бирже такая заявка может не реализоваться за раз и будут приходить цепочки ответов, "реализовано N акций". Такая сложные реакция сама может внутри себя содержать свою очередь событий. Однако присутствовать в основной очереди реакций просто как отдельная реакция.
2. Технология реализации (на .NET):
2.1. Способ линейного описания кода, содержащего асинхронные операции
Как я понимаю, готового средства в .NET нет (?). Есть возможность использования IObservable возвращаемых значений в качестве возвращаемых значений асинхронных функций и описания того самого линейного (!) кода с помощью LINQ выражения. Тут есть сомнения в наглядности и читаемости. gandjustas, как считаешь? Другой вариант — библиотека, предлагаемая Undying. Еще есть что-то полезное в .NET 4. Еще враианты? Забить на линейность кода, оперировать цепочками callback'ов.
В общем-то вопрос по большей части открыт. В частности я не до конца уверен, что RX хорош именно как средство для 2.1, скорее для 2.2. Возможно не прав.
2.2. Способ задания логики "реакций".
Как вариант "реакции" подписываются на входные события и в обработчиках анализируют очередь (например, с помощью RX) и что-то творят с очередью. Выкидывают себя оттуда, например.
2.3. Возможность сериализации текущего состояния модели для восстановления работы после прерывания.
Вопрос пока совсем открыт.
Re[23]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
S>2. Технология реализации (на .NET): S> 2.1. Способ линейного описания кода, содержащего асинхронные операции S> Как я понимаю, готового средства в .NET нет (?).
F# с его async, CCR, библиотека от Рихтера
Здравствуйте, Undying, Вы писали:
U>Хотя нет, я не прав, проблема, что у меня внутри асинхронного метода не будет ссылки на Task. Т.е. если пошаманить работать будет, а вот красивого решения пока что не вижу.
Изящное решение все же есть. Добавляем еще один вид Step:
class ServiceStep : WaitStep
{
public ServiceStep() : base(TimeSpan.Zero) {}
public void SetServiceInfo(LabeledThread thread, Task task);
public void ForceTask()
{
thread.ForceTask(task);
}
}
Т.е. получив в качестве результата такой Step, LabeledThread вызывает у него SetServiceInfo и передает ему ссылку на себя и на таску.
Здравствуйте, gandjustas, Вы писали:
U>>Цены на сколько видов товаров надо отслеживать? G>Самые тупые стратегии работают с одним, продвинутые — гораздо больше.
На нескольких десятков товаров особых проблем еще не будет, даже если их опрашивать с частотой в 10 мс. А вот если товаров будет хотя бы несколько тысяч, цена на них будет меняться относительно редко, но время отклика на изменение цены нужно будет минимальное, тогда да чисто временная модель больно много процессорного времени будет тратить в холостую. Совмещение временной модели с событийной тут будет гораздо лучше.
U>>В клиентском коде нужно добавить в обработчики событий вызовы метода ForceTask, полагаю тоже в полчаса уложиться можно. G>Ну вот уже больше похоже на async enumerator G>Вопрос номер два. Как дождаться двух событий?
В смысле двух событий? Продолжать выполнение только тогда, когда произошли оба события или что?
Re[27]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Цены на сколько видов товаров надо отслеживать? G>>Самые тупые стратегии работают с одним, продвинутые — гораздо больше.
U>На нескольких десятков товаров особых проблем еще не будет, даже если их опрашивать с частотой в 10 мс. А вот если товаров будет хотя бы несколько тысяч, цена на них будет меняться относительно редко, но время отклика на изменение цены нужно будет минимальное, тогда да чисто временная модель больно много процессорного времени будет тратить в холостую. Совмещение временной модели с событийной тут будет гораздо лучше.
Именно это и лучше.
Кстати вообще стоит минимизировать время отклика для любых систем, потому что входящие запросы складываются в очередь (так или иначе) и при большом времени отклика в среднем очередь рано или поздно переполнится.
U>>>В клиентском коде нужно добавить в обработчики событий вызовы метода ForceTask, полагаю тоже в полчаса уложиться можно. G>>Ну вот уже больше похоже на async enumerator G>>Вопрос номер два. Как дождаться двух событий?
U>В смысле двух событий? Продолжать выполнение только тогда, когда произошли оба события или что?
Уже бесcмысленно, ты понял где слабое место TaskPull.
Re[23]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
S>Текущий итог по подпроблемам и возможным решениям: S>1. Модель обработки. S> Пока мне ближе всего модель "входные события" + "очередь реакций".
Rx довольно успешно покрывает любую модель. TPL чуть хуже.
S> Причем реакция может быть "сложной", до своего завершения требовать прихода нескольких различных событий и отсылки ответных воздействий. Пример — реализация большого объема акций. На бирже такая заявка может не реализоваться за раз и будут приходить цепочки ответов, "реализовано N акций". Такая сложные реакция сама может внутри себя содержать свою очередь событий. Однако присутствовать в основной очереди реакций просто как отдельная реакция.
Видимо наоборот из нескольких событий "продано N акций" будет получаться событие "продан пакет акций". По факту никаких очередей не будет, буду асинхронные коллекции.
S>2. Технология реализации (на .NET): S> 2.1. Способ линейного описания кода, содержащего асинхронные операции S> Как я понимаю, готового средства в .NET нет (?).
TPL с Extras (с помощью итераторов), async в F# (монада).
S>Есть возможность использования IObservable возвращаемых значений в качестве возвращаемых значений асинхронных функций и описания того самого линейного (!) кода с помощью LINQ выражения.
Потому что LINQ тоже монада (синтаксический сахар для монад), но есть возможность также использовать итераторы. Пример
S>Тут есть сомнения в наглядности и читаемости. gandjustas, как считаешь?
Да, в отличие от F# и его computational expressions (читай async), выразительные способности LINQ гораздо ниже.
S>Другой вариант — библиотека, предлагаемая Undying. Еще есть что-то полезное в .NET 4. Еще враианты? Забить на линейность кода, оперировать цепочками callback'ов.
Абсолютно не вариант писать коллбеки, посмотри Мейера, он там детально про это рассказывает.
S>В общем-то вопрос по большей части открыт. В частности я не до конца уверен, что RX хорош именно как средство для 2.1, скорее для 2.2. Возможно не прав.
Rx одинаково хорош для всего, но надо иметь некоторую сноровку, поначалу мозг рвет.
S> 2.2. Способ задания логики "реакций". S> Как вариант "реакции" подписываются на входные события и в обработчиках анализируют очередь (например, с помощью RX) и что-то творят с очередью. Выкидывают себя оттуда, например.
Надо мудрствовать, тебе нужна функция IQueryable<T1> -> IQueryable<T2>, где T1 входные события, T2 — выходное состояние, возможно после выполнения некоторых действий. Оставайся в монаде и будет счастье.
S> 2.3. Возможность сериализации текущего состояния модели для восстановления работы после прерывания. S> Вопрос пока совсем открыт.
С помощью .Do комбинатора сохраняешь текущее состояние, а при старте приложение подсовываешь последнее сохраненное в качестве начального.
Re[6]: Good practice по алгоритмизации в условиях асинхронно
S>>Кстати, а вообще, касаемо Microsoft'овских рекомендаций, т.е. вот примерно такой способ (c RX и LINQ) MS и рекомендует для визуально синхронного описания по сути асинхронного кода? G>WF
советуешь его поизучать в контексте моих задач?
Re[6]: Good practice по алгоритмизации в условиях асинхронно
G>TPL с Extras (с помощью итераторов), async в F# (монада).
Где зрить про Extras?
G>Абсолютно не вариант писать коллбеки, посмотри Мейера, он там детально про это рассказывает.
В Twisted'е привык Только через некоторое время стал пользоваться его же "сахаром" с yield.
G>Надо мудрствовать, тебе нужна функция IQueryable<T1> -> IQueryable<T2>, где T1 входные события, T2 — выходное состояние, возможно после выполнения некоторых действий. Оставайся в монаде и будет счастье.
Кстати, что есть монада в данном контексте?
Re[24]: Good practice по алгоритмизации в условиях асинхронн
S>> Причем реакция может быть "сложной", до своего завершения требовать прихода нескольких различных событий и отсылки ответных воздействий. Пример — реализация большого объема акций. На бирже такая заявка может не реализоваться за раз и будут приходить цепочки ответов, "реализовано N акций". Такая сложные реакция сама может внутри себя содержать свою очередь событий. Однако присутствовать в основной очереди реакций просто как отдельная реакция. G>Видимо наоборот из нескольких событий "продано N акций" будет получаться событие "продан пакет акций". По факту никаких очередей не будет, буду асинхронные коллекции.
Про асинхронные коллекции — да.
В примере имею в виду, что действие может быть не атомарным, а многошаговым и высокоуровневым. Типа продавать до победного по рыночной цене.. И в основной очереди действий, например, захочется оперировать именно такими действиями. А каждое из них будет декомпозироваться на более мелкие шажки.
Re[25]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
G>>TPL с Extras (с помощью итераторов), async в F# (монада). S>Где зрить про Extras? http://blogs.msdn.com/b/pfxteam/archive/2010/04/04/9990342.aspx http://code.msdn.microsoft.com/ParExtSamples
G>>Абсолютно не вариант писать коллбеки, посмотри Мейера, он там детально про это рассказывает. S>В Twisted'е привык Только через некоторое время стал пользоваться его же "сахаром" с yield.
Ну суть в том что колбеки контекст теряют (читай не работают замыкания).
G>>Надо мудрствовать, тебе нужна функция IQueryable<T1> -> IQueryable<T2>, где T1 входные события, T2 — выходное состояние, возможно после выполнения некоторых действий. Оставайся в монаде и будет счастье. S>Кстати, что есть монада в данном контексте?
IQueryable<T>
Re[25]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
S>>> Причем реакция может быть "сложной", до своего завершения требовать прихода нескольких различных событий и отсылки ответных воздействий. Пример — реализация большого объема акций. На бирже такая заявка может не реализоваться за раз и будут приходить цепочки ответов, "реализовано N акций". Такая сложные реакция сама может внутри себя содержать свою очередь событий. Однако присутствовать в основной очереди реакций просто как отдельная реакция. G>>Видимо наоборот из нескольких событий "продано N акций" будет получаться событие "продан пакет акций". По факту никаких очередей не будет, буду асинхронные коллекции. S>Про асинхронные коллекции — да.
S>В примере имею в виду, что действие может быть не атомарным, а многошаговым и высокоуровневым. Типа продавать до победного по рыночной цене.. И в основной очереди действий, например, захочется оперировать именно такими действиями. А каждое из них будет декомпозироваться на более мелкие шажки.
Во, ты почувствовал Силу, называется она функциональное программирование.
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, Undying, Вы писали:
U>>Хотя нет, я не прав, проблема, что у меня внутри асинхронного метода не будет ссылки на Task. Т.е. если пошаманить работать будет, а вот красивого решения пока что не вижу.
U>Изящное решение все же есть. Добавляем еще один вид Step:
U>
U>class ServiceStep : WaitStep
U>{
U> public ServiceStep() : base(TimeSpan.Zero) {}
U> public void SetServiceInfo(LabeledThread thread, Task task);
U> public void ForceTask()
U> {
U> thread.ForceTask(task);
U> }
U>}
U>
U>Т.е. получив в качестве результата такой Step, LabeledThread вызывает у него SetServiceInfo и передает ему ссылку на себя и на таску.
То есть таки придется переписать чуть менее чем весь клиентский код.
U>Соответственно получаем возможность с легкостью комбинировать событийную и временную модель пробуждения потоков, используя их сильные стороны.
1)В чем сильная сторона пробуждения по времени?
2)Почему бы не сделать System.Threading.Timer для "времнной модели", а не делать ожидание в потоке исполнителе?
3)Как исполнитель будет узнавать о том что событие таки наступило?
Re[8]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>Пытаюсь подступить к RX со стороны биржевого симулятора.
S>На базе которого можно будет потом гонять какие-то свои примерчики стратегий на RX.
S>Но что-то тяжко идет..
S>Во-первых, почему-то не вижу элементов в OrderChanged, во-вторых не работает Delay.
S>В чем может быть дело?
S>
S> public enum OrderStatus
S> {
S> Sent,
S> Matched,
S> Canceled
S> }
S> class Order
S> {
S> public double Price;
S> public int Volume;
S> public OrderStatus Status;
S> }
S> class Trader
S> {
S> public IObservable<double> Ticks {get; private set; }
S> public IObservable<Order> OrderChanged { get; private set; }
S> private Action<Order> orderChanged;
S> public Trader()
S> {
S> var rnd = new Random();
S> this.Ticks = Observable.Defer(
S> () => Observable.Return(Math.Round(1500.0 + rnd.NextDouble() * 10, 2)))
S> //.Delay(TimeSpan.FromMilliSeconds(100 * rnd.NextDouble())))
S> .Repeat();//Используй GenerateWithTime
S> var subj = new Subject<Order>();
S> orderChanged += subj.OnNext;
S> this.OrderChanged = Observable.Defer(() => subj); //Defer тут не нужен
S> //this.OrderChanged = Observable.Defer(() => subj).Throttle(TimeSpan.FromMilliseconds(500 * rnd.NextDouble()));
S> }
S> public void RegisterOrder(Order order)
S> {
S> order.Status = OrderStatus.Matched;
S> orderChanged(order);
S> }
S> public void CancelOrder(Order order)
S> {
S> order.Status = OrderStatus.Canceled;
S> orderChanged(order);
S> }
S> }
S> class Program
S> {
S> static void Main(string[] args)
S> {
S> var trader = new Trader();
S> using (trader.Ticks.Subscribe(
S> tick =>
S> {
S> Console.WriteLine("Tick:" + tick);
S> if (tick > 1505)
S> trader.RegisterOrder(new Order());
S> },
S> ex => Console.WriteLine(ex),
S> () => Console.WriteLine("Completed")
S> ))
S> {
S> }
S> using (trader.OrderChanged.Subscribe(
S> order =>
S> {
S> Console.WriteLine("OrderChanged:", order.Status);
S> },
S> ex => Console.WriteLine(ex),
S> () => Console.WriteLine("Completed")
S> ))
S> {
S> }
S> Console.ReadKey();
S> }
S> }
S>
Пару комментов выделил.
Кроме того:
1)Не надо иметь состояние (order), лучше его передавать в IObservable
2)Не надо делать subscribe, надо использовать комбинаторы и находиться в монаде до последнего, то есть передавать IObservable пока это возможно.
Re[9]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, gandjustas, Вы писали:
U>>Соответственно получаем возможность с легкостью комбинировать событийную и временную модель пробуждения потоков, используя их сильные стороны. G>1)В чем сильная сторона пробуждения по времени?
Во-первых, по коду сразу же видна последовательность выполнения действий, соответственно хоть код и является асинхронным понимать его столь же легко, как и синхронный. В чисто событийной модели последовательность действий не очевидна, соответственно и понимать его сложно и баги связанные с неопределенностью последовательности вызова событий очень сложно недопускать и отлавливать.
Во-вторых, неважно как проходит информации об изменении, хоть в виде событий, хоть просто некая переменная где-то изменяется, хоть просто определенное время прошло, код остается принципиально одним и тем же. Соответственно с легкостью реализуются те же таймауты и т.п.
G>2)Почему бы не сделать System.Threading.Timer для "времнной модели", а не делать ожидание в потоке исполнителе?
А смысл? TaskPulling создает для своей работы указанное число потоков, поставить задачи в эти потоки можно только в виде Task, соответственно если Task'ов требующих выполнения в текущий момент нет, то делать ничего не надо и поток временно засыпает.
G>3)Как исполнитель будет узнавать о том что событие таки наступило?
Но что самое интересное с using но без Delay все работало, т.е. подписка не заканчивалась.
G>Кроме того: G>1)Не надо иметь состояние (order), лучше его передавать в IObservable
Это ты про что? Менять order.State в Do?
G>2)Не надо делать subscribe, надо использовать комбинаторы и находиться в монаде до последнего, то есть передавать IObservable пока это возможно.
Поясни plz.
Кстати, RegisterOrder и CancelOrder реализован через дополнительный event orderChanged и Subject. Есть другой способ пополнения IObservable? (без введения event'а)
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Соответственно получаем возможность с легкостью комбинировать событийную и временную модель пробуждения потоков, используя их сильные стороны. G>>1)В чем сильная сторона пробуждения по времени?
U>Во-первых, по коду сразу же видна последовательность выполнения действий, соответственно хоть код и является асинхронным понимать его столь же легко, как и синхронный. В чисто событийной модели последовательность действий не очевидна, соответственно и понимать его сложно и баги связанные с неопределенностью последовательности вызова событий очень сложно недопускать и отлавливать.
Если используется yield, то последовательность и так видна, коллбек нужен только для продолжения.
G>>2)Почему бы не сделать System.Threading.Timer для "времнной модели", а не делать ожидание в потоке исполнителе?
U>А смысл? TaskPulling создает для своей работы указанное число потоков, поставить задачи в эти потоки можно только в виде Task, соответственно если Task'ов требующих выполнения в текущий момент нет, то делать ничего не надо и поток временно засыпает.
Я уже писал что при ожидании по времени есть некоторый предел, связаный с точностью системного таймера, для колбеков — нет, поток будет спать ровно столько сколько нужно.
G>>3)Как исполнитель будет узнавать о том что событие таки наступило?
U>Что тебе непонятно в этом коде: http://rsdn.ru/forum/design/3931808.1.aspx
Здравствуйте, sergunok, Вы писали:
S>СПАСИБО!
S>Основная проблема была именно из-за using..
S>Но что самое интересное с using но без Delay все работало, т.е. подписка не заканчивалась.
G>>Кроме того: G>>1)Не надо иметь состояние (order), лучше его передавать в IObservable S>Это ты про что? Менять order.State в Do?
Вообще не надо иметь изменяемую переменную order
G>>2)Не надо делать subscribe, надо использовать комбинаторы и находиться в монаде до последнего, то есть передавать IObservable пока это возможно. S>Поясни plz.
Ну просто не писать Subscribe и использовать комбинаторы.
S>Кстати, RegisterOrder и CancelOrder реализован через дополнительный event orderChanged и Subject. Есть другой способ пополнения IObservable? (без введения event'а)
Конечно, RegisterOrder и CancelOrder сами могут возвращать IObservable.
Здравствуйте, gandjustas, Вы писали:
U>>А смысл? TaskPulling создает для своей работы указанное число потоков, поставить задачи в эти потоки можно только в виде Task, соответственно если Task'ов требующих выполнения в текущий момент нет, то делать ничего не надо и поток временно засыпает.
Ничего не понял. Как твой исходный вопрос этой ветки обсуждения:
G>2)Почему бы не сделать System.Threading.Timer для "времнной модели", а не делать ожидание в потоке исполнителе?
Связан с твоим дальнейшим ответом:
G>Я уже писал что при ожидании по времени есть некоторый предел, связаный с точностью системного таймера, для колбеков — нет, поток будет спать ровно столько сколько нужно.
G>>>2)Не надо делать subscribe, надо использовать комбинаторы и находиться в монаде до последнего, то есть передавать IObservable пока это возможно. S>>Поясни plz. G>Ну просто не писать Subscribe и использовать комбинаторы.
т.е. использовать extension методы RX и LINQ над IObserver?
S>>Кстати, RegisterOrder и CancelOrder реализован через дополнительный event orderChanged и Subject. Есть другой способ пополнения IObservable? (без введения event'а) G>Конечно, RegisterOrder и CancelOrder сами могут возвращать IObservable.
А как примерно будет выглядеть метод RegisterOrder с таким типом возвращаемого значения да еще и при условии, что исполнение заявки может происходить частями (т.е. будет несколько раз модифицироваться RealizedVolume в Order)?
public class Order
{
public int Id;
public double Price;
public int Volume;
public int RealizedVolume;public OrderStatus Status;
public override string ToString()
{
return string.Format("Order Id={0} Status={1}", Id, Status);
}
}
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>А смысл? TaskPulling создает для своей работы указанное число потоков, поставить задачи в эти потоки можно только в виде Task, соответственно если Task'ов требующих выполнения в текущий момент нет, то делать ничего не надо и поток временно засыпает.
U>Ничего не понял. Как твой исходный вопрос этой ветки обсуждения:
G>>2)Почему бы не сделать System.Threading.Timer для "времнной модели", а не делать ожидание в потоке исполнителе?
U>Связан с твоим дальнейшим ответом:
G>>Я уже писал что при ожидании по времени есть некоторый предел, связаный с точностью системного таймера, для колбеков — нет, поток будет спать ровно столько сколько нужно.
U>???
Никак.
Я просто не вижу смысла ждать определенное время. В 99.9% случаев надо продолжать выполнение после какого-то события, а тот 0.1% где действительно надо ждать некоторое время можно реализовать таймером.
U>>>Что тебе непонятно в этом коде: http://rsdn.ru/forum/design/3931808.1.aspx
? G>>Где будет вызываться SetServiceInfo для ServiceTask?
U>Под ServiceTask ты имеешь в виду ServiceStep?
Да.
U>SetServiceInfo будет вызываться в LabeledThread.ExecuteStep.
Уже понял, все равно придется править весь клиентский код для такого поведения.
ЗЫ. Кстати так и не увидел копирования потоков на TaskPull
Здравствуйте, gandjustas, Вы писали:
G>Я просто не вижу смысла ждать определенное время. В 99.9% случаев надо продолжать выполнение после какого-то события, а тот 0.1% где действительно надо ждать некоторое время можно реализовать таймером.
А, к примеру, отваливание по таймауту как делать?
U>>SetServiceInfo будет вызываться в LabeledThread.ExecuteStep. G>Уже понял, все равно придется править весь клиентский код для такого поведения.
Логично. Если хочешь использовать только временную модель, то о ForceTask ничего знать не надо. Если хочешь использовать еще и событийную, то надо будет добавить три строчки кода в каждую таску. В чем проблема-то?
G>ЗЫ. Кстати так и не увидел копирования потоков на TaskPull
Сейчас отвечу.
Re[28]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
U>>Чтения блока естественно выполняется синхронно, а как оно еще может выполняться? G>Асинхронно.
Что значит асинхронно? В смысле в другом потоке? Ну так поставь таску на чтение файла в другой поток, а не в основной и будет тебе счастье.
G>>>Надо использовать BeginRead\EndRead, BeginWrite\EndWrite. U>>Зачем? В чем будет преимущество? G>В том что поток на время чтения не будет блокироваться.
Он и так не блокируется. Можешь поставить чтение из нескольких файлов в один поток и оно будет выполняться параллельно, а не последовательно.
Re[13]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
G>>>>2)Не надо делать subscribe, надо использовать комбинаторы и находиться в монаде до последнего, то есть передавать IObservable пока это возможно. S>>>Поясни plz. G>>Ну просто не писать Subscribe и использовать комбинаторы. S>т.е. использовать extension методы RX и LINQ над IObserver?
над IObservable
S>>>Кстати, RegisterOrder и CancelOrder реализован через дополнительный event orderChanged и Subject. Есть другой способ пополнения IObservable? (без введения event'а) G>>Конечно, RegisterOrder и CancelOrder сами могут возвращать IObservable. S>А как примерно будет выглядеть метод RegisterOrder с таким типом возвращаемого значения да еще и при условии, что исполнение заявки может происходить частями (т.е. будет несколько раз модифицироваться RealizedVolume в Order)?
IObservable<RealizedVolume> например
Посмотри мои примеры, там ровно одни Subscribe, и ниче.
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>Я просто не вижу смысла ждать определенное время. В 99.9% случаев надо продолжать выполнение после какого-то события, а тот 0.1% где действительно надо ждать некоторое время можно реализовать таймером.
U>А, к примеру, отваливание по таймауту как делать?
1)Отваливание по таймауту обычно уже реализовано в асинхронном методе
2)Как уже писал: ждать события завершения или таймера.
U>>>SetServiceInfo будет вызываться в LabeledThread.ExecuteStep. G>>Уже понял, все равно придется править весь клиентский код для такого поведения.
U>Логично. Если хочешь использовать только временную модель, то о ForceTask ничего знать не надо. Если хочешь использовать еще и событийную, то надо будет добавить три строчки кода в каждую таску. В чем проблема-то?
В том что переписывать надо.
Re[29]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Чтения блока естественно выполняется синхронно, а как оно еще может выполняться? G>>Асинхронно.
U>Что значит асинхронно? В смысле в другом потоке?
Нет, для чтения с диска например вообще не нужны потоки, вот Stream и позволяет вызывать асинхронную операцию, которая для большинства стримов отобразится на асинхронную операцию в ОС, которая в свою очередь вообще не трогает потоки до завершения.
U>Ну так поставь таску на чтение файла в другой поток, а не в основной и будет тебе счастье.
Это не то.
G>>>>Надо использовать BeginRead\EndRead, BeginWrite\EndWrite. U>>>Зачем? В чем будет преимущество? G>>В том что поток на время чтения не будет блокироваться.
U>Он и так не блокируется.
Блокируется пока выполняется Read и Write.
U>Можешь поставить чтение из нескольких файлов в один поток и оно будет выполняться параллельно, а не последовательно.
нет, нужно именно один Stream
Здравствуйте, gandjustas, Вы писали:
U>>А, к примеру, отваливание по таймауту как делать? G>1)Отваливание по таймауту обычно уже реализовано в асинхронном методе
А если не реализовано? То ждать пока реализуют?
G>2)Как уже писал: ждать события завершения или таймера.
Какого таймера? И когда должен вызываться таймер, если операция у нас состоит из трех асинхронных шагов, у первого из которых таймаут 15 секунд, у второго — 5 минут, у третьего — 1 секунда?
U>>Логично. Если хочешь использовать только временную модель, то о ForceTask ничего знать не надо. Если хочешь использовать еще и событийную, то надо будет добавить три строчки кода в каждую таску. В чем проблема-то?
G>В том что переписывать надо.
А в альтернативных вариантах информация по какому событию надо завершать выполнение шага будет проброшена магически? Или для этого тоже придется код писать?
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>А, к примеру, отваливание по таймауту как делать? G>>1)Отваливание по таймауту обычно уже реализовано в асинхронном методе
U>А если не реализовано? То ждать пока реализуют?
G>>2)Как уже писал: ждать события завершения или таймера.
U>Какого таймера? И когда должен вызываться таймер, если операция у нас состоит из трех асинхронных шагов, у первого из которых таймаут 15 секунд, у второго — 5 минут, у третьего — 1 секунда?
Ну сделать 3 таймера, проблема?
U>>>Логично. Если хочешь использовать только временную модель, то о ForceTask ничего знать не надо. Если хочешь использовать еще и событийную, то надо будет добавить три строчки кода в каждую таску. В чем проблема-то?
G>>В том что переписывать надо.
U>А в альтернативных вариантах информация по какому событию надо завершать выполнение шага будет проброшена магически? Или для этого тоже придется код писать?
Ну я же приводил пример с чтением файла http://rsdn.ru/forum/design/3931077.aspx
Здравствуйте, gandjustas, Вы писали:
U>>Какого таймера? И когда должен вызываться таймер, если операция у нас состоит из трех асинхронных шагов, у первого из которых таймаут 15 секунд, у второго — 5 минут, у третьего — 1 секунда? G>Ну сделать 3 таймера, проблема?
Где? Вне описания задачи? И как ты собрался определять какой шаг сейчас выполняется? И вообще это очень понятный код, когда задача описывается в одном месте, а таймауты для шагов этой задачи в совершенно другом?
G>Явного ожидания нет, тем не менее между вызовом асинхронного чтения и завершением операции ни один поток блокирован не будет.
А процессорное время-то асинхронное чтение хоть потребляет? Или магически работает, на святом духе?
Re[30]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Нет, для чтения с диска например вообще не нужны потоки, вот Stream и позволяет вызывать асинхронную операцию, которая для большинства стримов отобразится на асинхронную операцию в ОС, которая в свою очередь вообще не трогает потоки до завершения.
А асинхронная операция в ОС она магическая или как? Ни потоков, ни процессорного времени не использует?
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Какого таймера? И когда должен вызываться таймер, если операция у нас состоит из трех асинхронных шагов, у первого из которых таймаут 15 секунд, у второго — 5 минут, у третьего — 1 секунда? G>>Ну сделать 3 таймера, проблема?
U>Где? Вне описания задачи? И как ты собрался определять какой шаг сейчас выполняется? И вообще это очень понятный код, когда задача описывается в одном месте, а таймауты для шагов этой задачи в совершенно другом?
С чего ты это взял?
Вообще проблема с таймаутами надумана, где они нужны они уже встроены в сами операции, а там где нет, то и не сильно надо.
G>>Явного ожидания нет, тем не менее между вызовом асинхронного чтения и завершением операции ни один поток блокирован не будет. U>А процессорное время-то асинхронное чтение хоть потребляет?
Нет
U>Или магически работает, на святом духе?
Чтобы прочитать с диска процессорное время не нужно. Диску подается команда "считать что-то" и указывается буфер куда положить результаты, по окончанию чтения контроллер диска сообщает что данные прочитаны. Аналогично с чтением данных из сети.
В этом и суть асинхронных операций. Для выполнения IO не нужно ни процессорное время, ни потоки, но синхронная модель программирования заставляет блокировать поток, ожидающий завершения. При большом количестве IO приходится плодить много потоков, что негативно сказывается на быстродействии, поэтому и используются асинхронные операции чтобы не плодить потоки.
Вообще-то странно что я тебе это объясняю.
Re[31]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>Нет, для чтения с диска например вообще не нужны потоки, вот Stream и позволяет вызывать асинхронную операцию, которая для большинства стримов отобразится на асинхронную операцию в ОС, которая в свою очередь вообще не трогает потоки до завершения.
U>А асинхронная операция в ОС она магическая или как? Ни потоков, ни процессорного времени не использует?
Использует только поток для сигнализации о завершении.
Re[14]: Good practice по алгоритмизации в условиях асинхронн
Снятие заявки наверное может быть каким-то таким (ниже). Но пока не очень понимаю как снятие остановит генерацию исполнений по этой заявке в RegisterOrder.
Здравствуйте, gandjustas, Вы писали:
G>В этом и суть асинхронных операций. Для выполнения IO не нужно ни процессорное время, ни потоки, но синхронная модель программирования заставляет блокировать поток, ожидающий завершения. При большом количестве IO приходится плодить много потоков, что негативно сказывается на быстродействии, поэтому и используются асинхронные операции чтобы не плодить потоки.
Ладно, признаю в случае чтения из файла код с AsyncRead будет чуточку эффективнее кода с обычным Read'ом. Но я не понимаю, чем пример с AsyncRead'ом будет принципиально отличаться от примеров: http://rsdn.ru/forum/design/3929522.1.aspx
Приведи чуть более полный код. Метод аналогичный ReadStartBytesFile на Rx как будет выглядеть?
G>Вообще проблема с таймаутами надумана, где они нужны они уже встроены в сами операции, а там где нет, то и не сильно надо.
Если полагать, что все асинхронные задачи за тебя реализовали дяденьки из микрософта, то да. А если асинхронно нужно решать реальные бизнес задачи, то совсем даже наоборот.
Re[16]: Good practice по алгоритмизации в условиях асинхронн
S>>gandjustas, покритикуй, пожалуйста, мой RegisterOrder (в симуляторе торговой системы).
G>Я уже приводил пример как надо сделать интерфейс для асинхронных операций, ты упорно не хочешь этого делать. Ничем более помочь не могу.
Твой пример изучил, но видимо в нем не весь код. Что есть выделенное жирным?:
public IObservable<OrderStatusEn> Buy(double price, int volume)
{
return Observable.CreateWithDisposable<OrderStatusEn>(obs =>
{
var order = new Order() { Price = price, Volume = volume };
this.trade.RegisterOrder(order);
var cancel = new CancellationDisposable();var isProcessed = false;
Action<Order> changed = null;
changed = o =>
{
if (o == order)
{
isProcessed = true;
obs.OnNext(o.Status);
obs.OnCompleted();
this.trade.OrderChanged -= changed;
}
};
this.trade.OrderChanged += changed;
cancel.Token.Register(() =>
{
if (!isProcessed) trade.CancelOrder(order);
});
return cancel;
});
}
Re[17]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
S>>>gandjustas, покритикуй, пожалуйста, мой RegisterOrder (в симуляторе торговой системы).
G>>Я уже приводил пример как надо сделать интерфейс для асинхронных операций, ты упорно не хочешь этого делать. Ничем более помочь не могу.
S>Твой пример изучил, но видимо в нем не весь код.
Весь
S>
S>public IObservable<OrderStatusEn> Buy(double price, int volume)
S> {
S> return Observable.CreateWithDisposable<OrderStatusEn>(obs =>
S> {
S> var order = new Order() { Price = price, Volume = volume };
S> this.trade.RegisterOrder(order);
S> var cancel = new CancellationDisposable();
S> var isProcessed = false;
S> Action<Order> changed = null;
S> changed = o =>
S> {
S> if (o == order)
S> {
S> isProcessed = true;
S> obs.OnNext(o.Status);
S> obs.OnCompleted();
S> this.trade.OrderChanged -= changed;
S> }
S> };
S> this.trade.OrderChanged += changed;
S> cancel.Token.Register(() =>
S> {
S> if (!isProcessed) trade.CancelOrder(order);
S> });
S> return cancel;
S> });
S> }
S>
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>В этом и суть асинхронных операций. Для выполнения IO не нужно ни процессорное время, ни потоки, но синхронная модель программирования заставляет блокировать поток, ожидающий завершения. При большом количестве IO приходится плодить много потоков, что негативно сказывается на быстродействии, поэтому и используются асинхронные операции чтобы не плодить потоки.
U>Ладно, признаю в случае чтения из файла код с AsyncRead будет чуточку эффективнее кода с обычным Read'ом.
Не просто чуточку, а дофига эффективнее, особенно под нагрузкой.
U>
U>IEnumerable<Step> ReadStartBytesFile(string filename)
U>{
U> ServiceStep serviceStep = new ServiceStep();
U> yield return serviceStep;
U> using (FileStream s = new FileStream(filename))
U> {
U> bool isCompleted = false;
U> s.BeginRead(buffer, 0, buffer.Length, delegate(IAsyncResult result)
U> {
U> isCompleted = true;
U> serviceStep.ForceTask();
U> }, null);
U> DateTime startTime = DateTime.UtcNow;
U> yield return new WaitStep(TimeSpan.FromSeconds(30), delegate
U> {
U> if (isCompleted)
U> return true;
U> if (DateTime.UtcNow - startTime > TimeSpan.FromSeconds(30))
U> throw new Exception("Timeout expired");
U> return false;
U> });
U> }
U> yield return new FinishStep(buffer);
U>}
U>
1)Не вызывается EndRead, это значит ты теряешь информацию об исключениях и количестве прочитаных байт.
2)Read и BeginRead может прочитать не столько сколько указано, а меньше, причем не только в случае окончания данных.
3)Таки копирование потоков надо.
4)Файл строит открывать с флагом асинхронности в конструкторе для таких целей.
G>>С чего ты это взял?
G>>В случае Rx будет выглядеть так G>>
U>Приведи чуть более полный код. Метод аналогичный ReadStartBytesFile на Rx как будет выглядеть?
Ну я бы всетаки сделал extension для Stream, который ровно заданное (!) количество читает байты.
public static IObservable<Unit> ReadExact(this Stream stream, byte[] buffer, int offset, int count)
{
return Observable.Iterate(() => ReadExactInternal(stream, buffer, offset, count));
}
public static IEnumerable<IObservable<object>> ReadExactInternal(Stream stream, byte[] buffer, int offset, int count)
{
var read = Observable.FromAsyncPattern<byte[], int, int, int>(stream.BeginRead, stream.EndRead);
while(count>0)
{
var result = read(buffer, offset, count).Start();
yield return result;
count -= result[0];
offset += result[0];
}
}
G>>Вообще проблема с таймаутами надумана, где они нужны они уже встроены в сами операции, а там где нет, то и не сильно надо.
U>Если полагать, что все асинхронные задачи за тебя реализовали дяденьки из микрософта, то да.
Я не предполагаю, я документацию смотрю.
U> А если асинхронно нужно решать реальные бизнес задачи, то совсем даже наоборот.
Ну для этого WF есть, со всеми enterprise фичами. Мы же говорим о высоконагруженных приложениях , где большой поток входящих данных и нужна быстрая реакция (АСУТП, автотрейдинг), а для серверов с множеством клиентов нужна еще экономия ресурсов.
Re[18]: Good practice по алгоритмизации в условиях асинхронн
G>Но проблема у тебя в том что у тебя сам интерфейс ITrade спроектирован неправильно.
К сожалению писан не мной.
Планирую как раз спроектировать правильный ITradeRX
и реализовать его в TradeRXSimulator и в ITrade2ITradeRXAdapter
Что-то типа:
public interface ITradeRX
{
public IObservable<double> Ticks {get; }
public IObservable<OrderState> RegisterOrder(Order order);
public IObservable<OrderState> CancelOrder(Order order);
}
Здравствуйте, gandjustas, Вы писали:
U>>Приведи чуть более полный код. Метод аналогичный ReadStartBytesFile на Rx как будет выглядеть? G>Ну я бы всетаки сделал extension для Stream, который ровно заданное (!) количество читает байты.
G>
G>public static IObservable<Unit> ReadExact(this Stream stream, byte[] buffer, int offset, int count)
G>{
G> return Observable.Iterate(() => ReadExactInternal(stream, buffer, offset, count));
G>}
G>public static IEnumerable<IObservable<object>> ReadExactInternal(Stream stream, byte[] buffer, int offset, int count)
G>{
G> var read = Observable.FromAsyncPattern<byte[], int, int, int>(stream.BeginRead, stream.EndRead);
G> while(count>0)
G> {
G> var result = read(buffer, offset, count).Start();
G> yield return result;
G> count -= result[0];
G> offset += result[0];
G> }
G>}
G>
И где здесь проверка таймаута?
Re[19]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
G>>Но проблема у тебя в том что у тебя сам интерфейс ITrade спроектирован неправильно. S>К сожалению писан не мной.
S>Планирую как раз спроектировать правильный ITradeRX S>и реализовать его в TradeRXSimulator и в ITrade2ITradeRXAdapter
S>Что-то типа: S>public interface ITradeRX S> { S> public IObservable<double> Ticks {get; } S> public IObservable<OrderState> RegisterOrder(Order order); S> public IObservable<OrderState> CancelOrder(Order order); S> }
Все равно плохо. У тебя RegisterOrder и CancelOrder работают с одним ордером, значит тебе придется этот ордер как-то передавать от одного вызова к другому. Ты продолжаешь держаться за этим методы, хотя они больше всего портят дизайн.
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
U>>>Приведи чуть более полный код. Метод аналогичный ReadStartBytesFile на Rx как будет выглядеть? G>>Ну я бы всетаки сделал extension для Stream, который ровно заданное (!) количество читает байты.
G>>
G>>public static IObservable<Unit> ReadExact(this Stream stream, byte[] buffer, int offset, int count)
G>>{
G>> return Observable.Iterate(() => ReadExactInternal(stream, buffer, offset, count));
G>>}
G>>public static IEnumerable<IObservable<object>> ReadExactInternal(Stream stream, byte[] buffer, int offset, int count)
G>>{
G>> var read = Observable.FromAsyncPattern<byte[], int, int, int>(stream.BeginRead, stream.EndRead);
G>> while(count>0)
G>> {
G>> var result = read(buffer, offset, count).Timeout(...).Start();
G>> yield return result;
G>> count -= result[0];
G>> offset += result[0];
G>> }
G>>}
G>>
U>И где здесь проверка таймаута?
Зачем?
Можно дописать, см выделенное.
Re[20]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, sergunok, Вы писали:
G>>>Но проблема у тебя в том что у тебя сам интерфейс ITrade спроектирован неправильно. S>>К сожалению писан не мной.
S>>Планирую как раз спроектировать правильный ITradeRX S>>и реализовать его в TradeRXSimulator и в ITrade2ITradeRXAdapter
S>>Что-то типа: S>>public interface ITradeRX S>> { S>> public IObservable<double> Ticks {get; } S>> public IObservable<OrderState> RegisterOrder(Order order); S>> public IObservable<OrderState> CancelOrder(Order order); S>> }
G>Все равно плохо. У тебя RegisterOrder и CancelOrder работают с одним ордером, значит тебе придется этот ордер как-то передавать от одного вызова к другому. Ты продолжаешь держаться за этим методы, хотя они больше всего портят дизайн.
Каким он может быть при условии, что снятие заявки до ее исполнения необходимо для некоторых стратегий?
Насколько я понимаю, в твоем примере с адаптером отдельной операции снятия, которая могла быть проинициирована стратегией, не было.
G>>>public static IObservable<Unit> ReadExact(this Stream stream, byte[] buffer, int offset, int count)
G>>>{
G>>> return Observable.Iterate(() => ReadExactInternal(stream, buffer, offset, count));
G>>>}
G>>>public static IEnumerable<IObservable<object>> ReadExactInternal(Stream stream, byte[] buffer, int offset, int count)
G>>>{
G>>> var read = Observable.FromAsyncPattern<byte[], int, int, int>(stream.BeginRead, stream.EndRead);
G>>> while(count>0)
G>>> {
G>>> var result = read(buffer, offset, count).Timeout(...).Start();
G>>> yield return result;
G>>> count -= result[0];
G>>> offset += result[0];
G>>> }
G>>>}
G>>>
U>>И где здесь проверка таймаута? G>Можно дописать, см выделенное.
Проверка таймаута нормально сделана. А если информация о окончании события будет приходить не по событию, а по изменению некоей переменной, тогда код как будет выглядеть?
И также интересует как выглядит FromAsyncPattern внутри, где можно код посмотреть?
Re[21]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, sergunok, Вы писали:
G>>>>Но проблема у тебя в том что у тебя сам интерфейс ITrade спроектирован неправильно. S>>>К сожалению писан не мной.
S>>>Планирую как раз спроектировать правильный ITradeRX S>>>и реализовать его в TradeRXSimulator и в ITrade2ITradeRXAdapter
S>>>Что-то типа: S>>>public interface ITradeRX S>>> { S>>> public IObservable<double> Ticks {get; } S>>> public IObservable<OrderState> RegisterOrder(Order order); S>>> public IObservable<OrderState> CancelOrder(Order order); S>>> }
G>>Все равно плохо. У тебя RegisterOrder и CancelOrder работают с одним ордером, значит тебе придется этот ордер как-то передавать от одного вызова к другому. Ты продолжаешь держаться за этим методы, хотя они больше всего портят дизайн.
S>Каким он может быть при условии, что снятие заявки до ее исполнения необходимо для некоторых стратегий?
1)Что вернет RegisterOrder в случае отмены операции?
2)Что вернет CancelOrder если заявка успеет реализоваться?
Ты с такими методами не сможешь псевдосинхронно записать дейсвтия хотя их последовательность линейна. Тебе понадобится тогда джоинить потоки событий (простыни кода), а не писать маленькие Linq запросы.
Здравствуйте, Undying, Вы писали:
U>Здравствуйте, gandjustas, Вы писали:
G>>>>
G>>>>public static IObservable<Unit> ReadExact(this Stream stream, byte[] buffer, int offset, int count)
G>>>>{
G>>>> return Observable.Iterate(() => ReadExactInternal(stream, buffer, offset, count));
G>>>>}
G>>>>public static IEnumerable<IObservable<object>> ReadExactInternal(Stream stream, byte[] buffer, int offset, int count)
G>>>>{
G>>>> var read = Observable.FromAsyncPattern<byte[], int, int, int>(stream.BeginRead, stream.EndRead);
G>>>> while(count>0)
G>>>> {
G>>>> var result = read(buffer, offset, count).Timeout(...).Start();
G>>>> yield return result;
G>>>> count -= result[0];
G>>>> offset += result[0];
G>>>> }
G>>>>}
G>>>>
U>>>И где здесь проверка таймаута? G>>Можно дописать, см выделенное.
U>Проверка таймаута нормально сделана. А если информация о окончании события будет приходить не по событию, а по изменению некоей переменной, тогда код как будет выглядеть?
Слабо представляю зачем такое, но тоже легко делается. GenerateWithTime + Where на изменение.
U>И также интересует как выглядит FromAsyncPattern внутри, где можно код посмотреть?
Качай Rx и рефлектором.
Re[22]: Good practice по алгоритмизации в условиях асинхронн
S>>Каким он может быть при условии, что снятие заявки до ее исполнения необходимо для некоторых стратегий? G>1)Что вернет RegisterOrder в случае отмены операции?
Canceled G>2)Что вернет CancelOrder если заявка успеет реализоваться?
Matched
G>Ты с такими методами не сможешь псевдосинхронно записать действия хотя их последовательность линейна. Тебе понадобится тогда джоинить потоки событий (простыни кода), а не писать маленькие Linq запросы.
Да, понимаю.. Но не очень вижу путь.
Re[23]: Good practice по алгоритмизации в условиях асинхронн
S>>>Каким он может быть при условии, что снятие заявки до ее исполнения необходимо для некоторых стратегий? G>>1)Что вернет RegisterOrder в случае отмены операции? S>Canceled
То есть и RegisterOrder вернет Canceled, CancelOrder вернет Canceled?
Реализуешь такое?
G>>2)Что вернет CancelOrder если заявка успеет реализоваться? S>Matched
Аналогично выше.
G>>Ты с такими методами не сможешь псевдосинхронно записать действия хотя их последовательность линейна. Тебе понадобится тогда джоинить потоки событий (простыни кода), а не писать маленькие Linq запросы. S>Да, понимаю.. Но не очень вижу путь.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, sergunok, Вы писали:
S>>>>Каким он может быть при условии, что снятие заявки до ее исполнения необходимо для некоторых стратегий? G>>>1)Что вернет RegisterOrder в случае отмены операции? S>>Canceled G>То есть и RegisterOrder вернет Canceled, CancelOrder вернет Canceled? G>Реализуешь такое?
Почему бы и нет.
И RegisterOrder и CancelOrder — асинхронные операции, причина завершения каждой из них не обязательно единственна.
G>>>2)Что вернет CancelOrder если заявка успеет реализоваться? S>>Matched G>Аналогично выше.
G>>>Ты с такими методами не сможешь псевдосинхронно записать действия хотя их последовательность линейна. Тебе понадобится тогда джоинить потоки событий (простыни кода), а не писать маленькие Linq запросы. S>>Да, понимаю.. Но не очень вижу путь.
Получается маленькие запросы могут быть в случае, например, двух потоков "тики", "изменение статуса заявки"?
G>http://msdn.microsoft.com/en-us/library/ms228969(v=VS.90).aspx G>http://msdn.microsoft.com/en-us/library/ms228969(v=VS.100).aspx
Предлагаешь забить на RX и реализовывать асинхронные операции по MS рекомендациям?
Не понимаю, как конкретно это поможет проблемам с в случае использования потоков IObservable.
Проблема в том, что я не знаю RX и мучаю тебя вопросами или RX тут не очень подходит (для функциональности: "новый тик", "регистрация заявки", "снятие заявки")
Re[25]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, sergunok, Вы писали:
S>>>>>Каким он может быть при условии, что снятие заявки до ее исполнения необходимо для некоторых стратегий? G>>>>1)Что вернет RegisterOrder в случае отмены операции? S>>>Canceled G>>То есть и RegisterOrder вернет Canceled, CancelOrder вернет Canceled? G>>Реализуешь такое? S>Почему бы и нет. S>И RegisterOrder и CancelOrder — асинхронные операции, причина завершения каждой из них не обязательно единственна.
Не стоит так делать.
G>>http://msdn.microsoft.com/en-us/library/ms228969(v=VS.90).aspx G>>http://msdn.microsoft.com/en-us/library/ms228969(v=VS.100).aspx
S>Предлагаешь забить на RX и реализовывать асинхронные операции по MS рекомендациям?
Следовать рекомендациям надо, rx только упростит работу, никакой магии в нем нету. Он не сделает кривой интерфейс более правильным.
S>Проблема в том, что я не знаю RX и мучаю тебя вопросами или RX тут не очень подходит (для функциональности: "новый тик", "регистрация заявки", "снятие заявки")
Да все подходит, только не надо думать что Rx все проблемы решит. Без грамотного интефейса никакой Rx не поможет.
Re[4]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>И куча if'ов в трех коллбеках, в привязке к состоянию.
На мой взгляд, причина Ваших проблем заключается в попытке создать сложную асинхронную систему на основе событийной модели. За основу для организации кода Вы выбираете события (заявка выполнена, заявка снята и др.), "навешиваете" на них обработчики и сталкиваетесь с проблемой, что код в каждом обработчике не является линейным, а, наоборот, содержит множество ветвлений, которые, к тому же, могут повторяться в разных обработчиках.
Вы это понимаете и сами пишите:
S>Таким образом в событие тика необходимо проверять какова цена текущей заявки, если она отличается от того, что нужно, инициировать снятие, при этом реально может придти событие как снятия так и исполнения (причем полного или неполного) и уже после успешного снятия выставить свою заявку.. S>Пhичем если процесс снятия уже инициирован, новый тик не должен бомбить биржу повторными снятиями.
S>У меня в голове сразу возникает понятие переменной с состояниями типа: S>- заявка выставлена S>- жду снятия
S>И куча if'ов в трех коллбеках, в привязке к состоянию.
Причина if'ов в коллбеках, как я уже писал выше, заключается в том, что Вы основываете код на событиях, т.е. за основу для группировки (классификации — называйте, как хотите) выбираете условия, а не действия, которые по этим условия выполняются.
Чтобы избежать излишних ветвлений, нужно, наоборот, строить код не на основе событий, а на основе элементарных операций, которые могут быть выполнены.
Например, в Вашем примере возможны такие операции:
Получение и обновление информации о товаре.
Получение и обновление информации о заявке.
Подача заявки.
Отмена заявки.
Принятие решения о подаче или отмене заявки.
И т.д.
Если дизайн всей программы построить вокруг этих операций, то код получится простым и не будет содержать глубоких ветвлений.
Как это сделать? Приведу один из возможных вариантов...
Для выполнения каждой операции заводим отдельный поток и даем ему осмысленное название. Получатся 5 потоков (они же — 5 сущностей):
Обновляльщик цен
Обновляльщик заявок
Подаватель заявок
Отменяльщик заявок
Принимальщик решений
Опишем кратко назначение каждого из потоков.
Обновляльщик цен получает биржевой тик и меняет информацию о ценах в таблице товаров:
Товар
Предыдущая цена
Идентификатор тика, когда была установлена предыдущая цена
Текущая цена
Идентификатор тика, когда была установлена текущая цена Обновляльщик заявок получает события "заявка выполнена", "заявка отменена" и на основе этих событий модифицирует таблицу заявок:
Идентификатор заявки
Идентификатор товара
Заявленная цена
Статус Подаватель заявок перебирает записи в таблице заявок и подает те заявки, которые нужно подать.
Отменяльщик заявок отменяет заявки.
Принимальщик решений просматривает таблицу товаров и таблицу заявок. В зависимости от изменения цен на товары модифицирует таблицу заявок (например, изменяет статус заявок для тех, которые нужно подать или которые нужно отменить).
Вот и всё!
Приведённое решение не является единственным. Например, некоторые потоки можно объединить. Например, можно объединить Подаватель заявок и Отменяльщик заявок.
Сами потоки тоже можно организовать по-разному. Например, они могут "просыпаться" по событию или же по времени. Например, Принимальщик решений может просматривать таблицу товаров раз в какое-то время, а Обновляльщик цен — ждать прихода очередного тика. Тут все зависит от конкретных условий задачи, например, от протокола обмена данными с сервером.
КЛ>Причина if'ов в коллбеках, как я уже писал выше, заключается в том, что Вы основываете код на событиях, т.е. за основу для группировки (классификации — называйте, как хотите) выбираете условия, а не действия, которые по этим условия выполняются. КЛ>Чтобы избежать излишних ветвлений, нужно, наоборот, строить код не на основе событий, а на основе элементарных операций, которые могут быть выполнены.
Вы правы!!! Примерно в середине этого обсуждения пришел к тому, что:
1. привязываться нужно именно к действиям
2. грамотно распределять обработку этих действий по классам (старые добрые процедурные и ОО подходы)
При этом на текущий момент планирую оперировать не просто одним текущим действием, а очередью действий (первое из которых является текущим, а остальные должны быть выполнены после его завершения), поскольку в реальных стратегиях приходится некоторые действия ставить в очередь.
В примере из поста у меня получаются такие:
— Ожидание
— Реализация заявки
— Снятие заявки
Благодарю Вас за участие в обсуждении!
Re[5]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Для выполнения каждой операции заводим отдельный поток и даем ему осмысленное название. Получатся 5 потоков (они же — 5 сущностей):
Это называется actor model.
Фактически один большой КА со сложным состоянием разбивается на несколько автоматов попроще, которые обмениваются сообщениями.
Недостаток — сложно отлаживать.
Есть другой вариант уменьшения сложности: отказаться от состояния и заниматься преобразованием потоков событий, особенно хорошо получается если часть преобразований можно выполнить "псевдосихронно", те код выглядит линейно, но в моменты ожидания поток не блокируется.
Таким образом значимое состояние будет передаваться в событиях, а то что не нужно (локальное состояние) будет спрятано внутри преобразований.
Такое сложнее писать (как зачастую сложнее писать функциональный код, по сравнению с императивным), но прекрасно отлаживатеся (логами на любом этапе преобразований) и тестируется (вместо потока событий подсовывается заранее заготовленный массив).
КЛ>Тут все зависит от конкретных условий задачи, например, от протокола обмена данными с сервером.
Кстати очень верное замечание, от него вообще говоря плясать надо.
Если протокол оперирует только двумя статусами заявки, типа Matched и Cancelled (хотя по факту еще еще переходные), то автомат, отвечающий за состояния, нужно держать на клиенте (это и будет actor).
А вот на потоки опираться не стоит, есть другие хорошие способы реализации actor model в .NET.
Re[6]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, gandjustas, Вы писали:
КЛ>>Для выполнения каждой операции заводим отдельный поток и даем ему осмысленное название. Получатся 5 потоков (они же — 5 сущностей): G>Это называется actor model.
Если "буржуйская" википедия не обманывает, то Actor Model — нечто совершенно другое, а не то, что я имел в виду. Русский термин Агентно-ориентированный подход — тоже из другой оперы.
Сразу скажу, отличие, на мой взгляд, принципиальное: Actor Model предполагает концентрацию на сущностях (акторах, агентах — называйте, как хотите), которые между собой взаимодействуют посредством обмена сообщениями, а я призываю прежде всего концентрироваться не на сущностях, а на элементарных операциях, которые нужно выполнить. Понятное дело, что далее операции будут делегированы отдельным сущностям и, при необходимости, могут быть распараллелены и выполняться в разных потоках. Но прежде всего — это операции и группировка их в последовательные или параллельные "цепочки".
Сам я такой подход называю "конвейерным", потому что он напоминает проектирование технологического процесса для массового или серийного производства, а готовый результат напоминает конвейер. Общее (и в программировании, и в производстве) — это избавление от ветвлений и обратных связей. Например, на производстве не допускается выполнение черновой операции над заготовкой после выполнения чистовой: сначала должны быть выполнены все черновые операции и только после этого — чистовые. Точно так же и в приведённом примере: нельзя подавать новую заявку на тот же товар до успешного снятия предыдущей заявки.
Логика событийной модели подсказывает, что в такой ситуации новую заявку нужно подавать в обработчике события Заявка Отменена. Но поскольку предыдущей заявки может и не быть, то код подачи заявки нужно будет продублировать и в обработчике события Биржевой Тик, а, возможно, и в обработчике события Заявка Удовлетворена Частично. Получается дублирование кода, а также — изобилие условных операторов, т.к. одни и те же условия приходится проверять в обработчиках разных событий. Если же концентрироваться на операциях (например, на операции Подача Новой Заявки), то такой проблемы не возникает.
Actor Model тоже не даёт приемлемого результата, т.к. не отвечает на вопрос Как находить акторов? В некотором роде, Actor Model напоминает OOD с его расплывчатыми рекомендациями по нахождению кандидатов в классы. Отличием является лишь то, что в C++ одни объекты вызывают методы других объектов, а в Actor Model одни акторы посылают сообщения другим акторам.
G>Фактически один большой КА со сложным состоянием разбивается на несколько автоматов попроще, которые обмениваются сообщениями. G>Недостаток — сложно отлаживать.
Подозреваю, что сложность в отладке возникает именно из-за ошибок при выявлении акторов. Если грамотно расписать технологический процесс, как это принято у технологов на предприятиях, то код получается линейным и легко отлаживаемым. За последнее время я использовал конвейерную модель в 6-ти проектах (четырёх GPS-навигационных системах и двух консольных играх) и везде код получался достаточно простым.
Здравствуйте, sergunok, Вы писали:
S>При этом на текущий момент планирую оперировать не просто одним текущим действием, а очередью действий (первое из которых является текущим, а остальные должны быть выполнены после его завершения), поскольку в реальных стратегиях приходится некоторые действия ставить в очередь.
Было бы здорово, если бы Вы привели пару примеров таких стратегий. Вне конкретной задачи очень сложно что-то рекомендовать...
S>В примере из поста у меня получаются такие: S>- Ожидание S>- Реализация заявки S>- Снятие заявки
Тут опять же нужна конкретика. Пока непонятно, как будет выглядеть вся система в целом, если она будет основана на этих действиях.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, gandjustas, Вы писали:
КЛ>>>Для выполнения каждой операции заводим отдельный поток и даем ему осмысленное название. Получатся 5 потоков (они же — 5 сущностей): G>>Это называется actor model.
КЛ>Если "буржуйская" википедия не обманывает, то Actor Model — нечто совершенно другое, а не то, что я имел в виду. Русский термин Агентно-ориентированный подход — тоже из другой оперы.
КЛ>Сразу скажу, отличие, на мой взгляд, принципиальное: Actor Model предполагает концентрацию на сущностях (акторах, агентах — называйте, как хотите), которые между собой взаимодействуют посредством обмена сообщениями, а я призываю прежде всего концентрироваться не на сущностях, а на элементарных операциях, которые нужно выполнить. Понятное дело, что далее операции будут делегированы отдельным сущностям и, при необходимости, могут быть распараллелены и выполняться в разных потоках. Но прежде всего — это операции и группировка их в последовательные или параллельные "цепочки".
КЛ>Сам я такой подход называю "конвейерным", потому что он напоминает проектирование технологического процесса для массового или серийного производства, а готовый результат напоминает конвейер. Общее (и в программировании, и в производстве) — это избавление от ветвлений и обратных связей. Например, на производстве не допускается выполнение черновой операции над заготовкой после выполнения чистовой: сначала должны быть выполнены все черновые операции и только после этого — чистовые. Точно так же и в приведённом примере: нельзя подавать новую заявку на тот же товар до успешного снятия предыдущей заявки.
Видимо я начал комментировать реализацию, не поняв что имеется ввиду.
КЛ>Логика событийной модели подсказывает, что в такой ситуации новую заявку нужно подавать в обработчике события Заявка Отменена. Но поскольку предыдущей заявки может и не быть, то код подачи заявки нужно будет продублировать и в обработчике события Биржевой Тик, а, возможно, и в обработчике события Заявка Удовлетворена Частично. Получается дублирование кода, а также — изобилие условных операторов, т.к. одни и те же условия приходится проверять в обработчиках разных событий. Если же концентрироваться на операциях (например, на операции Подача Новой Заявки), то такой проблемы не возникает.
Тут непонятно. Как концентрация на операции Подача Новой Заявки поможет избежать дублирования кода, если эта операция может быть вызвана при наступлении разных событий?
КЛ>Actor Model тоже не даёт приемлемого результата, т.к. не отвечает на вопрос Как находить акторов? В некотором роде, Actor Model напоминает OOD с его расплывчатыми рекомендациями по нахождению кандидатов в классы. Отличием является лишь то, что в C++ одни объекты вызывают методы других объектов, а в Actor Model одни акторы посылают сообщения другим акторам.
Ну так и есть. Кроме того некоторые считают что actor model это труЪ ООП, как завещал Кей. Но в твоем посте акторы уже определены. Поэтому я и завел про них разговор.
G>>Фактически один большой КА со сложным состоянием разбивается на несколько автоматов попроще, которые обмениваются сообщениями. G>>Недостаток — сложно отлаживать.
КЛ>Подозреваю, что сложность в отладке возникает именно из-за ошибок при выявлении акторов.
Возможно.
КЛ>Если грамотно расписать технологический процесс, как это принято у технологов на предприятиях, то код получается линейным и легко отлаживаемым. За последнее время я использовал конвейерную модель в 6-ти проектах (четырёх GPS-навигационных системах и двух консольных играх) и везде код получался достаточно простым.
Ну это как раз и будут преобразования потоков событий, про которые я говорил.
Re[8]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, gandjustas, Вы писали:
G>Тут непонятно. Как концентрация на операции Подача Новой Заявки поможет избежать дублирования кода, если эта операция может быть вызвана при наступлении разных событий?
Концентрация на операции даёт нам критерий для классификации (или группировки). Не секрет, что общепринятым подходом является группировка по событиям или по условиям. В Event-based модели программист прежде всего выделяет команды (посылаемые серверу) и события (получаемые от сервера) и только затем задумывается о том, какими должны быть обработчики.
В нашем случае, топикастер приводит такие команды и события:
S>API, которое имеется:
S>
При написании не асинхронного, а обычного кода в качестве основы для группировки нередко выступают не события, а условия. В результате код превращается в мешанину из операторов if:
if (A)
{
if (B)
{
if (!C)
do1();
do2();
}
else
{
if (C)
do1();
else
do3();
do4();
}
}
else
{
if (B)
{
if (!C)
do1();
do5();
}
else
{
if (C)
do1();
else
do6();
do2();
}
}
Если же за основу взять операцию, то нужно поступать противоположно: группировать не операции под условие, а условия под операцию. В нашем случае, для операции Подача Новой Заявки нужно просто перечислить все условия, при которых она будет выполняться, и объединить эти условия при помощи союзов "И" или "ИЛИ".
Очевидно, такими условиями будут:
1) Произошло изменение цены на товар
и
2) Новая Заявка не подана
и
3) Отсутствует текущая заявка на товар
или
3а) Текущая заявка на товар отменена
или
3б) Текущая заявка на товар удовлетворена не в полном объёме
Соответственно, основная часть программы будет представлять собой цикл:
Каждая из бизнес-операций в этом цикле будет проверять условия её выполнения и в случае их выполнения — совершать требуемые действия. Условия можно складировать в одной или нескольких таблицах (см. моё самое первое сообщение в этой теме), например, в Таблице Товаров и в Таблице Заявок.
Заполняться же эти таблицы могут в обработчиках событий: Биржевой Тик, Заявка Удовлетворена, Заявка Отменена. Но и об этих обработчиках лучше думать тоже не как об обработчиках, а как об элементарных операциях:
Обновление информации о ценах
Обновление статуса заявок
При таком подходе эти обработчики можно будет реализовать как через события, так и аналогично основному циклу программы:
S>>Предлагаешь забить на RX и реализовывать асинхронные операции по MS рекомендациям? G>Следовать рекомендациям надо, rx только упростит работу, никакой магии в нем нету. Он не сделает кривой интерфейс более правильным.
S>>Проблема в том, что я не знаю RX и мучаю тебя вопросами или RX тут не очень подходит (для функциональности: "новый тик", "регистрация заявки", "снятие заявки") G>Да все подходит, только не надо думать что Rx все проблемы решит. Без грамотного интефейса никакой Rx не поможет.
Хмм.. Непонятно. Интерфейс библиотеки для доступа к данным биржи таков, какой есть. Честно говоря, не вижу в нем больших огрехов. Кроме этого, его можно много во что адаптировать.
Посмотрел про MS паттерны по асинхронной обработке — ничего принципиально нового в них не нашел. Эти паттерны даже больше похожи на гайдлайны. непонятно, чем они конкретно помогут.
А с применимости RX для подобных задач вопрос по-прежнему открыт. Под применением имею в виду не в коем случае не "лекарство от всех проблем", а скорее использование в качестве базового средства для обработки данных как по тикам, так и по заявкам и другим потокам, м.б. в какой-то степени для использования RX в качестве замены как раз способов организации асинхронности из MS паттернов (метод, стартующий операцию + event'ы, сигнализирующие о завершении).
То, в чем нет сомнения, так это то, что RX хорош для обработки "простого" потока однотипных событий. Поэкспериментировал, считать различные производные индикаторы на тиках — одно удовольствие.
Re[27]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, sergunok, Вы писали:
S>>>Предлагаешь забить на RX и реализовывать асинхронные операции по MS рекомендациям? G>>Следовать рекомендациям надо, rx только упростит работу, никакой магии в нем нету. Он не сделает кривой интерфейс более правильным.
S>>>Проблема в том, что я не знаю RX и мучаю тебя вопросами или RX тут не очень подходит (для функциональности: "новый тик", "регистрация заявки", "снятие заявки") G>>Да все подходит, только не надо думать что Rx все проблемы решит. Без грамотного интефейса никакой Rx не поможет.
S>Хмм.. Непонятно. Интерфейс библиотеки для доступа к данным биржи таков, какой есть. Честно говоря, не вижу в нем больших огрехов.
А я вижу. Он не оперирует состоянием заявки, поэтому необходимо писать stateful код.
S>А с применимости RX для подобных задач вопрос по-прежнему открыт.
Подойди с другой стороны. Попробуй найти решение получше.
S>То, в чем нет сомнения, так это то, что RX хорош для обработки "простого" потока однотипных событий. Поэкспериментировал, считать различные производные индикаторы на тиках — одно удовольствие.
Ну а простые потоки можно преобразовывать и join_ить. Это примерно как работа с SQL, есть несколько простых операций: проекция, фильтрация, группировка, соединение, а запросы можно нарисовать какие угодно.
Я об этом и говорю что нужна сноровка чтобы сходу писать код с помощью Rx.
Re[9]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Если же за основу взять операцию, то нужно поступать противоположно: группировать не операции под условие, а условия под операцию. В нашем случае, для операции Подача Новой Заявки нужно просто перечислить все условия, при которых она будет выполняться, и объединить эти условия при помощи союзов "И" или "ИЛИ".
Вот с этого места поподробнее, как объединить условия союзом "И". Условия получаются из событий.
КЛ>Очевидно, такими условиями будут:
КЛ>1) Произошло изменение цены на товар КЛ>и КЛ>2) Новая Заявка не подана КЛ>и КЛ>3) Отсутствует текущая заявка на товар КЛ>или КЛ>3а) Текущая заявка на товар отменена КЛ>или КЛ>3б) Текущая заявка на товар удовлетворена не в полном объёме
КЛ>Соответственно, основная часть программы будет представлять собой цикл:
КЛ>
КЛ>Каждая из бизнес-операций в этом цикле будет проверять условия её выполнения и в случае их выполнения — совершать требуемые действия. Условия можно складировать в одной или нескольких таблицах (см. моё самое первое сообщение в этой теме), например, в Таблице Товаров и в Таблице Заявок.
КЛ>Заполняться же эти таблицы могут в обработчиках событий: Биржевой Тик, Заявка Удовлетворена, Заявка Отменена. Но и об этих обработчиках лучше думать тоже не как об обработчиках, а как об элементарных операциях:
КЛ> КЛ>Обновление информации о ценах КЛ>Обновление статуса заявок КЛ>
КЛ>При таком подходе эти обработчики можно будет реализовать как через события, так и аналогично основному циклу программы:
КЛ>
КЛ>Соответственно, каждый из обработчиков запускается в отдельном потоке.
Вот и получились агенты (actor_ы), которые обмениваются информацией через разделяемое изменяемое состояние.
Это, как ни странно, самый худший вариант решения потому что:
1)Обращение к mutable state требует синхронизации
2)На каждый тип заявки будет создано по одному потоку, что при большом количестве заявок плохо скажется на производительности
3)Любой вариант ожидания по времени под высокой нагрузкой не работает из-за низкой точности системного таймера
4)pull модель оповещения из внешней системы чревата тем что при определенной нагрузке очередь не будет успевать разгребаться и могут начаться необъяснимые глюки, которые сложно отловить при дебаге.
Вообще твой вариант хорошо работает при низкой, желательно фиксированной нагрузке и ни разу не масштабируется.
Твое решение хорошее с точки зрения анализа, но ужасное с точки зрения реализации. А топик начался именно с реализации.
Re[10]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
G>Вот с этого места поподробнее, как объединить условия союзом "И". Условия получаются из событий.
Непонятен вопрос. Вы хотите узнать:
1) Как из событий получить условия?
2) Или же — как упростить условный оператор, если условий будет слишком много?
3) Или же что-то иное?
G>Вот и получились агенты (actor_ы), которые обмениваются информацией через разделяемое изменяемое состояние.
Да, но хочу отметить, что агенты — это результат проектирования. Моё возражение было против названия метода (Actor Model или агентно-ориентированное проектирование), т.к. такое название даёт неправильные ориентиры проектировщику, и он (вольно или невольно) пытается отыскать агенты, вместо того, чтобы выявлять операции.
G>Это, как ни странно, самый худший вариант решения потому что: G>1)Обращение к mutable state требует синхронизации
Вы слегка поторопились с таким выводом. Мы еще не дошли до конкретной реализации, которая будет воплощена в коде. Можно сделать так, чтобы к таблицам (товаров, заявок и др.) действительно обращались все потоки, а можно сделать и по-другому: к таблицам будет обращаться только основной поток, в котором выполняется бизнес-логика, а задания для остальных потоков рассылаются через очереди сообщений.
G>2)На каждый тип заявки будет создано по одному потоку, что при большом количестве заявок плохо скажется на производительности
Зачем? Мы же договорились, что группировку будем выполнять по операциям, а не по условиям или объектам (в Вашем случае — заявкам).
G>3)Любой вариант ожидания по времени под высокой нагрузкой не работает из-за низкой точности системного таймера
Вы думаете события ОС срабатывают как-то иначе? Шедулер тоже раз в определенное время проверяет флаг срабатывания события и, если флаг установлен, то "будит" ожидающий поток.
G>4)pull модель оповещения из внешней системы чревата тем что при определенной нагрузке очередь не будет успевать разгребаться и могут начаться необъяснимые глюки, которые сложно отловить при дебаге.
Под pull моделью Вы очевидно понимаете то, что "получальщики" новых цен или заявок сами инициируют запросы к серверу? Тут я с Вами вынужден не согласиться, т.к. если Ваши обработчики, которые вызываются по event'ам, тоже не справляются с нагрузкой, то возникает та же самая проблема. Если входной поток событий (тиков, event'ов) чрезвычайно насыщен, Вам нужно задуматься о масштабировании системы в любом случае, независимо от того, какую модель (pull или push) Вы используете.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, gandjustas, Вы писали:
G>>Вот с этого места поподробнее, как объединить условия союзом "И". Условия получаются из событий. КЛ>Непонятен вопрос. Вы хотите узнать:
КЛ>1) Как из событий получить условия? КЛ>2) Или же — как упростить условный оператор, если условий будет слишком много? КЛ>3) Или же что-то иное?
Больше интересует первое.
G>>Это, как ни странно, самый худший вариант решения потому что: G>>1)Обращение к mutable state требует синхронизации КЛ>Вы слегка поторопились с таким выводом. Мы еще не дошли до конкретной реализации, которая будет воплощена в коде. Можно сделать так, чтобы к таблицам (товаров, заявок и др.) действительно обращались все потоки, а можно сделать и по-другому: к таблицам будет обращаться только основной поток, в котором выполняется бизнес-логика, а задания для остальных потоков рассылаются через очереди сообщений.
На PDC08 был отличный доклад о быстродействии и масштабируемости систем. Там очень хорошо описано "откуда ноги растут". http://channel9.msdn.com/pdc2008/TL38/
Вкратце суть: есть две модели общения push — когда клиенту приходит событие, и pull — когда клиент сам запрашивает данные.
Когда преобразуется push в pull в каком-либо звене передачи, то появляются такие message loop_ы, а когда наоборот — буферы.
Читается слева направо, справа сервер, слева клиент. Схема двойственна, там где для сервера push, там для клиента pull и наоборот.
Чтобы добиться максимальной производительности надо чтобы во всей цепи передачи данных стрелки были направлены в одну сторону.
Если взять обычный веб-сервер, то для него схема выглядит так: (клиент) -> (сервер) <- (worker thread) -> (приложение на сервере). worker thread нужен потому что код приложения выполняется синхронно и нельзя допустить чтобы сервер "курил" пока обрабатыватеся один запрос.
А вот Node.js попытался сделать такую схему (клиент) -> (сервер) -> (приложение на сервере на js) за счет интерпретируемости js его можно выполнять асинхронно.
Теперь смотрим: торговый сервер push_ит клиентам тики, агент складывает эти данные в буфер, клиенты оттуда pull_ят данные. Далее предлагается сделать одного агента, который pull_ит состояния и push_ит задания другим агентам.
Итого схема выглядит так (исполнитель команд) -> (очередь команд) <- (основной поток с бизнес-логикой) -> (обработчик событий) <- (сервер).
С помощью Rx можно сделать так: (исполнитель команд) <- (обработчик событий) <- (сервер). А если эта схема не будет оперировать изменяемым состоянием, то отлаживать её станет элементарно. На событиях тоже можно так сделать, но тогда сильно перемешается код обработчика и исполнителя, будет куча if_ов и некоторое состояние (такое обычно называют конечным автоматом).
Но есть маленькая проблема, сервер не передает достаточно данных чтобы мог работать обработчик событий, а все необходимые данные появляются после выполнения команды. Поэтому три варианта:
1) смешать все вместе (вариант с событиями)
2) сделать выходной поток событий исполнителя еще одним входным потоком обработчика (как я приводил с помощью Rx). Получается неочевидный код.
3) Изолировать состояние в stateful агенте, который принимает одни события и плюется другими. Ухудшается отладка.
Именно этот вопрос на самом деле беспокоит автора (и Rx тут совершенно не при чем)
Реализовывать все на stateful агентах — далеко не самое лучшее решение.
G>>2)На каждый тип заявки будет создано по одному потоку, что при большом количестве заявок плохо скажется на производительности КЛ>Зачем? Мы же договорились, что группировку будем выполнять по операциям, а не по условиям или объектам (в Вашем случае — заявкам).
А потому что циклы Отменить-Отправить-ДождатьсяЗавершения несинхронно для разных заявок работают.
G>>3)Любой вариант ожидания по времени под высокой нагрузкой не работает из-за низкой точности системного таймера КЛ>Вы думаете события ОС срабатывают как-то иначе? Шедулер тоже раз в определенное время проверяет флаг срабатывания события и, если флаг установлен, то "будит" ожидающий поток.
А я не говорил про событие ОС. Асинхронные операции (начиная с чтения из сетевого потока) немного по-другому делаются. И вообще на потоки лучше не завязываться.
Re[12]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
КЛ>>1) Как из событий получить условия? КЛ>>2) Или же — как упростить условный оператор, если условий будет слишком много? КЛ>>3) Или же что-то иное? G>Больше интересует первое.
Путем сохранения событий в некотором хранилище. Например, информацию о заявках и о событиях, произошедших с ними, можно хранить в таблице заявок. Об этом я уже писал здесь
.
G>Чтобы добиться максимальной производительности надо чтобы во всей цепи передачи данных стрелки были направлены в одну сторону.
В целом, я согласен. Но если рассматривать конкретные примеры, то возникают вопросы (см. ниже).
G>А вот Node.js попытался сделать такую схему (клиент) -> (сервер) -> (приложение на сервере на js) за счет интерпретируемости js его можно выполнять асинхронно.
На мой взгляд, схема тут не однонаправленная, а двунаправленная. Не смотря на то, что Клиент инициирует запрос к Серверу, после запроса он все равно входит в режим ожидания, т.к. Клиенту нужен ответ. А ожидание — это и цикл, и Sleep. Если ответ считывается синхронно, то цикл будет выполняться в исходном потоке, если асинхронно — то в другом. Но суть от этого не меняется. Связь двунаправленная.
Аналогичная ситуация получается и со второй частью схемы: Сервер -> Приложение на JS. Чтобы отослать данные Клиенту, Серверу все равно их надо получить от Приложения. А это означает, что ему нужно не только посылать запросы к Приложению, но и каким-то образом получать от него ответы. Опять-таки получается цикл (например, что очередь событий не пуста) и ожидание (если делать нечего).
G>Теперь смотрим: торговый сервер push_ит клиентам тики, агент складывает эти данные в буфер, клиенты оттуда pull_ят данные. Далее предлагается сделать одного агента, который pull_ит состояния и push_ит задания другим агентам. G>Итого схема выглядит так (исполнитель команд) -> (очередь команд) <- (основной поток с бизнес-логикой) -> (обработчик событий) <- (сервер).
Мне кажется, схема все-таки будет немного другая:
Принимальщик Решений работает как с таблицей товаров (цен), так и с таблицей заявок. У него два источника данных. Поскольку данные (цены и информация о статусах заявок) поступают с разной частотой, то Принимальщик Решений крутится в своём потоке и работает со своей заданной частотой, независимо от частоты обновления цен и частоты поступления событий о статусах заявок.
В принципе, при необходимости Принимальщик Решений можно объединить с Обновляльщиком Цен и синхронизировать его работу с тиками от Сервера:
Если Обновляльщик Цен, Обновляльщик Заявок, Принимальщик Решений, Подавальщик/Отменяльщик Заявок выполняются на одном компе (т.е. для обмена данными между ними нет сетевого взаимодействия), то не вижу вообще никаких проблем с производительностью, т.к. уже писал, что системный шедулер использует тот же самый системный таймер, и любая асинхронная модель — это тот же самый отдельный поток со своим циклом ожидания.
G>С помощью Rx можно сделать так: (исполнитель команд) <- (обработчик событий) <- (сервер). А если эта схема не будет оперировать изменяемым состоянием, то отлаживать её станет элементарно. На событиях тоже можно так сделать, но тогда сильно перемешается код обработчика и исполнителя, будет куча if_ов и некоторое состояние (такое обычно называют конечным автоматом).
Насколько я понял топикастера (ну и суть задачи из его описания), проблема будет существовать, не зависимо от используемой технологии, т.к. у Принимальщика Решений (Вы его называете исполнитель команд) не один, а два (или даже три) источника данных:
Биржевой Тик
События: заявка удовлетворена и заявка отклонена
И чтобы не дублировать код (и не посылать одни и те же заявки на сервер) нужно:
(1) либо запустить Принимальщик Решений в отдельном потоке со своей частотой, которая не зависит от частоты поступления событий;
(2) либо синхронизировать работу Принимальщика Решений с частотой поступления какого-либо события (я предложил синхронизировать с биржевым тиком)
И, мне кажется, что по-другому все равно никак не сделать, не зависимо от используемой технологии. Даже если технология будет сама создавать необходимые потоки, сама отвечать за синхронизацию доступа к данным и прятать все это от программиста.
G>Но есть маленькая проблема, сервер не передает достаточно данных чтобы мог работать обработчик событий, а все необходимые данные появляются после выполнения команды. Поэтому три варианта: G>1) смешать все вместе (вариант с событиями)
На мой взгляд, не вариант, т.к. в этом случае действительно ухудшается отладка и сопровождение. Плюс будет дублирование кода.
G>2) сделать выходной поток событий исполнителя еще одним входным потоком обработчика (как я приводил с помощью Rx). Получается неочевидный код.
Не понимаю, зачем. (Если можете объяснить без Rx, то будет хорошо, т.к. я на .NET не программирую).
G>3) Изолировать состояние в stateful агенте, который принимает одни события и плюется другими. Ухудшается отладка.
В принципе, да. Но не сильно. Если грамотно сделать декомпозицию, то Принимальщик Решений, Обновляльщик Цен и Обновляльщик Заявок можно вообще отлаживать независимо.
G>>>2)На каждый тип заявки будет создано по одному потоку, что при большом количестве заявок плохо скажется на производительности КЛ>>Зачем? Мы же договорились, что группировку будем выполнять по операциям, а не по условиям или объектам (в Вашем случае — заявкам). G>А потому что циклы Отменить-Отправить-ДождатьсяЗавершения несинхронно для разных заявок работают.
Как мне представляется, это и не важно. Если Принимальщик Решений будет работать со своей частотой (или будет согласован с частотой биржевого тика).
G>А я не говорил про событие ОС. Асинхронные операции (начиная с чтения из сетевого потока) немного по-другому делаются. И вообще на потоки лучше не завязываться.
Вне зависимости от апи, реализуются-то они всё равно через thread'ы, mutex'ы и event'ы. Хотя согласен, что термин thread при описании концептуального решения (т.е. решения, не привязанного к определенной технологии) не очень хорош. Но мне пока не удается подобрать какой-то другой более адекватный термин.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, gandjustas, Вы писали:
КЛ>>>1) Как из событий получить условия? КЛ>>>2) Или же — как упростить условный оператор, если условий будет слишком много? КЛ>>>3) Или же что-то иное? G>>Больше интересует первое.
КЛ>Путем сохранения событий в некотором хранилище. Например, информацию о заявках и о событиях, произошедших с ними, можно хранить в таблице заявок. Об этом я уже писал здесь
.
G>>Чтобы добиться максимальной производительности надо чтобы во всей цепи передачи данных стрелки были направлены в одну сторону. КЛ>В целом, я согласен. Но если рассматривать конкретные примеры, то возникают вопросы (см. ниже).
G>>А вот Node.js попытался сделать такую схему (клиент) -> (сервер) -> (приложение на сервере на js) за счет интерпретируемости js его можно выполнять асинхронно. КЛ>На мой взгляд, схема тут не однонаправленная, а двунаправленная. Не смотря на то, что Клиент инициирует запрос к Серверу, после запроса он все равно входит в режим ожидания, т.к. Клиенту нужен ответ.
Это да
КЛ>А ожидание — это и цикл, и Sleep.
А это — нет. В этом и суть хорошей асинхронной обработки что для ожидания ничего не нужно. Фактически клиент отправив запрос(ы) на сервер рождает поток событий-ответов. Ответы также push_атся в клиента и он их обрабатывает. Причем каждый ответ — асинхронный (!) поток байт.
КЛ>Аналогичная ситуация получается и со второй частью схемы: Сервер -> Приложение на JS. Чтобы отослать данные Клиенту, Серверу все равно их надо получить от Приложения. А это означает, что ему нужно не только посылать запросы к Приложению, но и каким-то образом получать от него ответы. Опять-таки получается цикл (например, что очередь событий не пуста) и ожидание (если делать нечего).
И снова нет. Приложение на JS "слушает" поток входящих запросов и отдает поток ответов.
Если приложению делать нечего, то оно проваливается в ожидание ОС. Которое обычно реализуется командой hlt, отключающей процессор до прерывания.
G>>Теперь смотрим: торговый сервер push_ит клиентам тики, агент складывает эти данные в буфер, клиенты оттуда pull_ят данные. Далее предлагается сделать одного агента, который pull_ит состояния и push_ит задания другим агентам. G>>Итого схема выглядит так (исполнитель команд) -> (очередь команд) <- (основной поток с бизнес-логикой) -> (обработчик событий) <- (сервер). КЛ>Мне кажется, схема все-таки будет немного другая:
КЛ>
Ну тогда я хочу посмотреть на реализацию. Без дополнительных библиотек такое сделать сложно.
Если (Таблица Товаров) и (Таблица Заявок) не сами передают данные Принимателю решений, то стрелки будет в другую сторону.
Кроме того нужно учитывать что Принимальщик Решений будет опираться на сочетание состояний из таблиц, поэтому как будет организована push модель в таком случае даже представит не могу.
КЛ>Принимальщик Решений работает как с таблицей товаров (цен), так и с таблицей заявок. У него два источника данных. Поскольку данные (цены и информация о статусах заявок) поступают с разной частотой, то Принимальщик Решений крутится в своём потоке и работает со своей заданной частотой, независимо от частоты обновления цен и частоты поступления событий о статусах заявок.
Таки pull понадобится чтобы не зависеть от частоты источников.
КЛ>В принципе, при необходимости Принимальщик Решений можно объединить с Обновляльщиком Цен и синхронизировать его работу с тиками от Сервера:
КЛ>
КЛ>Если Обновляльщик Цен, Обновляльщик Заявок, Принимальщик Решений, Подавальщик/Отменяльщик Заявок выполняются на одном компе (т.е. для обмена данными между ними нет сетевого взаимодействия), то не вижу вообще никаких проблем с производительностью, т.к. уже писал, что системный шедулер использует тот же самый системный таймер, и любая асинхронная модель — это тот же самый отдельный поток со своим циклом ожидания.
G>>С помощью Rx можно сделать так: (исполнитель команд) <- (обработчик событий) <- (сервер). А если эта схема не будет оперировать изменяемым состоянием, то отлаживать её станет элементарно. На событиях тоже можно так сделать, но тогда сильно перемешается код обработчика и исполнителя, будет куча if_ов и некоторое состояние (такое обычно называют конечным автоматом).
КЛ>Насколько я понял топикастера (ну и суть задачи из его описания), проблема будет существовать, не зависимо от используемой технологии,
Проблема то будет, а её решение будет сильно зависеть от выбранных средств и дизайна.
КЛ>у Принимальщика Решений (Вы его называете исполнитель команд) не один, а два (или даже три) источника данных:
КЛ> КЛ>Биржевой Тик КЛ>События: заявка удовлетворена и заявка отклонена КЛ>
Именно 3, потому что должно учитываться текущее состояние заявки, которое генерируется Подавальщиком/Отменяльщиком Заявок.
КЛ>И чтобы не дублировать код (и не посылать одни и те же заявки на сервер) нужно:
КЛ>(1) либо запустить Принимальщик Решений в отдельном потоке со своей частотой, которая не зависит от частоты поступления событий;
Плохо, потому что частоты входящих событий не фиксированы.
КЛ>(2) либо синхронизировать работу Принимальщика Решений с частотой поступления какого-либо события (я предложил синхронизировать с биржевым тиком)
А я бы предложил заниматься преобразованием потоков, чтобы получить на входе Принимальщика Решений именно те данные, которые нужны и именно тогда когда они появятся.
Это называется join_ить потоки событий.
КЛ>И, мне кажется, что по-другому все равно никак не сделать, не зависимо от используемой технологии. Даже если технология будет сама создавать необходимые потоки, сама отвечать за синхронизацию доступа к данным и прятать все это от программиста.
Ну вообще-то нет, потому что используемые средства (языки\библиотеки) влияют на дизайн решений.
G>>Но есть маленькая проблема, сервер не передает достаточно данных чтобы мог работать обработчик событий, а все необходимые данные появляются после выполнения команды. Поэтому три варианта: G>>1) смешать все вместе (вариант с событиями) КЛ>На мой взгляд, не вариант, т.к. в этом случае действительно ухудшается отладка и сопровождение. Плюс будет дублирование кода.
Дублирования можно банально избежать с помощью отдельных методов, а дублирования условий — созданием явных состояний (но их может стать очень много).
G>>2) сделать выходной поток событий исполнителя еще одним входным потоком обработчика (как я приводил с помощью Rx). Получается неочевидный код. КЛ>Не понимаю, зачем. (Если можете объяснить без Rx, то будет хорошо, т.к. я на .NET не программирую).
Без Rx не смогу, потому что именно он вводит понятие потока событий (асинхронной коллекции). Вкратце если для обычной коллекции (списка) у нас есть операция получения итератора и прохода по коллекции в одном направлении с помощью этого итератора (последовательного получения всех элементов), то для асинхронной коллекции получает дуальная схема — мы подписываемся на события с помощью нашего наблюдателя, который что-либо делает при получении следующего элемента.
Для обычной коллекции можно написать комбинаторы, которые возвращают новую коллекцию (коллекции ленивые) итератор которой будет выполнять некоторые преобразования над итератором исходного списка (например пропускать элементы, применять для них функции итд). Аналогичное можно сделать и с асинхронным коллекциями и поиметь ту же саму семантику (theorems for free).
G>>3) Изолировать состояние в stateful агенте, который принимает одни события и плюется другими. Ухудшается отладка. КЛ>В принципе, да. Но не сильно. Если грамотно сделать декомпозицию, то Принимальщик Решений, Обновляльщик Цен и Обновляльщик Заявок можно вообще отлаживать независимо.
G>>>>2)На каждый тип заявки будет создано по одному потоку, что при большом количестве заявок плохо скажется на производительности КЛ>>>Зачем? Мы же договорились, что группировку будем выполнять по операциям, а не по условиям или объектам (в Вашем случае — заявкам). G>>А потому что циклы Отменить-Отправить-ДождатьсяЗавершения несинхронно для разных заявок работают. КЛ>Как мне представляется, это и не важно. Если Принимальщик Решений будет работать со своей частотой (или будет согласован с частотой биржевого тика).
Ну тогда будет много буферов и лупов, которые плохо скажутся на производительности. Кроме того фиксированная частота работает в среднем плохо, потому что частоты входящих потоков не фиксированы и варьируются в очень широком диапазоне.
G>>А я не говорил про событие ОС. Асинхронные операции (начиная с чтения из сетевого потока) немного по-другому делаются. И вообще на потоки лучше не завязываться. КЛ>Вне зависимости от апи, реализуются-то они всё равно через thread'ы, mutex'ы и event'ы. Хотя согласен, что термин thread при описании концептуального решения (т.е. решения, не привязанного к определенной технологии) не очень хорош. Но мне пока не удается подобрать какой-то другой более адекватный термин.
Если под словом thread имеется ввиду системный thread, то это неверно. Если поток исполнения, который может быть как system thread, fiber, так и сопрограммой (coroutine) или (в более общем случае) цепочкой продолжений (continuation), то тут обсуждать нечего. Поток исполнения всегда присутствует в том или ином виде. Хотя например при задании dataflow явного потока нет.
Re: Good practice по алгоритмизации в условиях асинхронности
Все равно все сведется к автомату, ИМХО. Т.е. данные появляются асинхронно, и при определенных условиях (наборе данных) ты должен выполнить определенное действие.
То же самое с сериализацией — при десериализации ты логично "двигаешь" автомат в нужную точку последовательностью входящих данных.
Словом, КА — просто и надежно. Только его нужно сразу выявить, формализовать (куда-нибудь записать диаграммку) и реализовать на ранних стадиях, пока не наворочена неформализованная куча -)
Re[2]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, asimonenko, Вы писали:
A>Все равно все сведется к автомату, ИМХО. Т.е. данные появляются асинхронно, и при определенных условиях (наборе данных) ты должен выполнить определенное действие.
A>То же самое с сериализацией — при десериализации ты логично "двигаешь" автомат в нужную точку последовательностью входящих данных.
A>Словом, КА — просто и надежно. Только его нужно сразу выявить, формализовать (куда-нибудь записать диаграммку) и реализовать на ранних стадиях, пока не наворочена неформализованная куча -)
Для автоматов нет способов борьбы со сложностью. Если состояний станет слишком много, то не не будет возможности как-то исправить.
Re[14]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
КЛ>>А ожидание — это и цикл, и Sleep. G>А это — нет. В этом и суть хорошей асинхронной обработки что для ожидания ничего не нужно. Фактически клиент отправив запрос(ы) на сервер рождает поток событий-ответов. Ответы также push_атся в клиента и он их обрабатывает. Причем каждый ответ — асинхронный (!) поток байт.
Даже если "забыть" про то, что данные по сети передаются не потоками, а пакетами, а иллюзию потока создаёт протокол транспортного уровня (например, TCP), который получает пакеты протокола сетевого уровня, проверяет их целостность, сортирует в порядке отправления, отправляет запросы на испорченные или не дошедшие пакеты, и всё это он делает не сам по себе, а, естественно, в системном потоке, которым, как и остальными системными потоками, управляет системный шедулер (это к вопросу о точности системного таймера и потерях времени на переключении потоков).
Даже если мы "забываем" про всю эту "кухню" и считаем, что у нас просто есть "поток байт", он же сам по себе не возьмётся из ниоткуда. Асинхронная процедура чтения этого потока байт как раз и заключается в том, что существует некий поток, скрытый от программиста, который периодически проверяет, пришли ли новые байты, и если они не пришли, то "засыпает" на некоторое время, а если пришли — отсылает их получателю.
КЛ>>Аналогичная ситуация получается и со второй частью схемы: Сервер -> Приложение на JS. Чтобы отослать данные Клиенту, Серверу все равно их надо получить от Приложения. А это означает, что ему нужно не только посылать запросы к Приложению, но и каким-то образом получать от него ответы. Опять-таки получается цикл (например, что очередь событий не пуста) и ожидание (если делать нечего). G>И снова нет. Приложение на JS "слушает" поток входящих запросов и отдает поток ответов.
Простейший вопрос: после отсылки запроса приложению JS, сервер пытается прочесть ответы. Что происходит, если ответы ещё не дошли?
На мой взгляд, возможны 3 варианта:
1) Сервер впадает в цикл ожидания до тех пор, пока не придет ответ.
2) Сервер использует асинхронную процедуру чтения, т.е. создает вспомогательный поток, который и ждет ответа, а сам занимается своими делами.
3) Сервер занимается своими делами и периодически пытается прочитать ответ: если ответ пришел, то обрабатывает его; если не пришел, то продолжает заниматься другими делами (та же асинхронность, но вместо вытесняющей используется кооперативная многозадачность).
G>Если приложению делать нечего, то оно проваливается в ожидание ОС. Которое обычно реализуется командой hlt, отключающей процессор до прерывания.
Оно же не отключает процессор сервера?
G>Ну тогда я хочу посмотреть на реализацию. Без дополнительных библиотек такое сделать сложно.
Вы не могли бы конкретизировать, что Вам непонятно?
G>Если (Таблица Товаров) и (Таблица Заявок) не сами передают данные Принимателю решений, то стрелки будет в другую сторону.
Даже если обмен данными между потоками происходит через очередь сообщений, Принимальщик Решений все равно должен извлечь сообщение из очереди для того, чтобы его обработать.
КЛ>>у Принимальщика Решений (Вы его называете исполнитель команд) не один, а два (или даже три) источника данных: КЛ>> КЛ>>Биржевой Тик КЛ>>События: заявка удовлетворена и заявка отклонена КЛ>>G>Именно 3, потому что должно учитываться текущее состояние заявки, которое генерируется Подавальщиком/Отменяльщиком Заявок.
Я писал о внешних источниках — не о внутренних.
КЛ>>(1) либо запустить Принимальщик Решений в отдельном потоке со своей частотой, которая не зависит от частоты поступления событий; G>Плохо, потому что частоты входящих событий не фиксированы.
Чем плохо?
КЛ>>(2) либо синхронизировать работу Принимальщика Решений с частотой поступления какого-либо события (я предложил синхронизировать с биржевым тиком) G>А я бы предложил заниматься преобразованием потоков, чтобы получить на входе Принимальщика Решений именно те данные, которые нужны и именно тогда когда они появятся. G>Это называется join_ить потоки событий.
Как это практически будет реализовываться, если частоты поступления событий разные? Да и некоторые события могут вообще не поступить?
G>Без Rx не смогу, потому что именно он вводит понятие потока событий (асинхронной коллекции). Вкратце если для обычной коллекции (списка) у нас есть операция получения итератора и прохода по коллекции в одном направлении с помощью этого итератора (последовательного получения всех элементов), то для асинхронной коллекции получает дуальная схема — мы подписываемся на события с помощью нашего наблюдателя, который что-либо делает при получении следующего элемента.
Понимаю, что это может сократить код при реализации, но не понимаю, как это влияет на проектирование и на выработку концептуального решения?
G>Ну тогда будет много буферов и лупов, которые плохо скажутся на производительности. Кроме того фиксированная частота работает в среднем плохо, потому что частоты входящих потоков не фиксированы и варьируются в очень широком диапазоне.
Не думаю, что обычное приложение с тремя-пятью потоками, выполняемое под Windows на процессоре с 2-4 ядрами как-то резко скажется на производительности. Соответственно, буферов и лупов тоже будет немного — по числу потоков.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, gandjustas, Вы писали:
КЛ>>>А ожидание — это и цикл, и Sleep. G>>А это — нет. В этом и суть хорошей асинхронной обработки что для ожидания ничего не нужно. Фактически клиент отправив запрос(ы) на сервер рождает поток событий-ответов. Ответы также push_атся в клиента и он их обрабатывает. Причем каждый ответ — асинхронный (!) поток байт.
КЛ>Даже если "забыть" про то, что данные по сети передаются не потоками, а пакетами, а иллюзию потока создаёт протокол транспортного уровня (например, TCP), который получает пакеты протокола сетевого уровня, проверяет их целостность, сортирует в порядке отправления, отправляет запросы на испорченные или не дошедшие пакеты, и всё это он делает не сам по себе, а, естественно, в системном потоке, которым, как и остальными системными потоками, управляет системный шедулер (это к вопросу о точности системного таймера и потерях времени на переключении потоков).
КЛ>Даже если мы "забываем" про всю эту "кухню" и считаем, что у нас просто есть "поток байт", он же сам по себе не возьмётся из ниоткуда. Асинхронная процедура чтения этого потока байт как раз и заключается в том, что существует некий поток, скрытый от программиста, который периодически проверяет, пришли ли новые байты, и если они не пришли, то "засыпает" на некоторое время, а если пришли — отсылает их получателю.
Ну правильно, поток блокируется на время выполнения пересылки получателю. А остальное время он может заниматься другими делами, в том числе отправлять пакеты другим получателям. Почти всегда хватает одного системного потока на процессор.
КЛ>>>Аналогичная ситуация получается и со второй частью схемы: Сервер -> Приложение на JS. Чтобы отослать данные Клиенту, Серверу все равно их надо получить от Приложения. А это означает, что ему нужно не только посылать запросы к Приложению, но и каким-то образом получать от него ответы. Опять-таки получается цикл (например, что очередь событий не пуста) и ожидание (если делать нечего). G>>И снова нет. Приложение на JS "слушает" поток входящих запросов и отдает поток ответов.
КЛ>Простейший вопрос: после отсылки запроса приложению JS, сервер пытается прочесть ответы. Что происходит, если ответы ещё не дошли?
А сервер не пытается прочесть, js приложение push_ает ответ серверу, а тот в сою очередь push_ает ответ клиенту. КЛ>На мой взгляд, возможны 3 варианта:
КЛ>1) Сервер впадает в цикл ожидания до тех пор, пока не придет ответ. КЛ>2) Сервер использует асинхронную процедуру чтения, т.е. создает вспомогательный поток, который и ждет ответа, а сам занимается своими делами. КЛ>3) Сервер занимается своими делами и периодически пытается прочитать ответ: если ответ пришел, то обрабатывает его; если не пришел, то продолжает заниматься другими делами (та же асинхронность, но вместо вытесняющей используется кооперативная многозадачность).
Второй вариант, толь он по сути не создает потоков, а использует небольшой пул.
G>>Если приложению делать нечего, то оно проваливается в ожидание ОС. Которое обычно реализуется командой hlt, отключающей процессор до прерывания. КЛ>Оно же не отключает процессор сервера?
Отключает.
G>>Ну тогда я хочу посмотреть на реализацию. Без дополнительных библиотек такое сделать сложно. КЛ>Вы не могли бы конкретизировать, что Вам непонятно?
Вот эта цепочка.
G>>Если (Таблица Товаров) и (Таблица Заявок) не сами передают данные Принимателю решений, то стрелки будет в другую сторону. КЛ>Даже если обмен данными между потоками происходит через очередь сообщений, Принимальщик Решений все равно должен извлечь сообщение из очереди для того, чтобы его обработать.
КЛ>>>у Принимальщика Решений (Вы его называете исполнитель команд) не один, а два (или даже три) источника данных: КЛ>>> КЛ>>>Биржевой Тик КЛ>>>События: заявка удовлетворена и заявка отклонена КЛ>>>G>>Именно 3, потому что должно учитываться текущее состояние заявки, которое генерируется Подавальщиком/Отменяльщиком Заявок. КЛ>Я писал о внешних источниках — не о внутренних.
А чем они хуже\лучше внутренних?
КЛ>>>(1) либо запустить Принимальщик Решений в отдельном потоке со своей частотой, которая не зависит от частоты поступления событий; G>>Плохо, потому что частоты входящих событий не фиксированы. КЛ>Чем плохо?
Тем что в среднем неэффективно получится, или реакция будет запаздывать, или ресурсы прожигаться будут. Так или иначе масштабируемость ухудшится.
КЛ>>>(2) либо синхронизировать работу Принимальщика Решений с частотой поступления какого-либо события (я предложил синхронизировать с биржевым тиком) G>>А я бы предложил заниматься преобразованием потоков, чтобы получить на входе Принимальщика Решений именно те данные, которые нужны и именно тогда когда они появятся. G>>Это называется join_ить потоки событий. КЛ>Как это практически будет реализовываться, если частоты поступления событий разные?
Вот так
Observable.Join(
//Если новая минимальная цена и ожидаем события, то покупаем
minPrices.And(idleState).Then((p, _) => Buy(p)),
//Если новая минимальная цена и покупаем, то отменяем
minPrices.And(buyState)
.Then((p, s) =>
{
this.trade.CancelOrder(s.Order);
return new State(null, StateEn.CancellingBuy, p);
}),
//Если статус заявки изменился на matched и покупаем, то переходим в ожидание
orderMatched.And(buyState).Then((o, s) => new State(null, StateEn.Idle, s.Price)),
//Если статус заявки изменился и отменяем, то покупаем по новой цене
orderChanged.And(cancellingState).Then((_, s) => Buy(s.Price))
)
КЛ>Да и некоторые события могут вообще не поступить?
Тогда и заджоиненное не наступит.
G>>Без Rx не смогу, потому что именно он вводит понятие потока событий (асинхронной коллекции). Вкратце если для обычной коллекции (списка) у нас есть операция получения итератора и прохода по коллекции в одном направлении с помощью этого итератора (последовательного получения всех элементов), то для асинхронной коллекции получает дуальная схема — мы подписываемся на события с помощью нашего наблюдателя, который что-либо делает при получении следующего элемента. КЛ>Понимаю, что это может сократить код при реализации, но не понимаю, как это влияет на проектирование и на выработку концептуального решения?
Потому что вводится новая абстракция (первоклассная сущность) — поток событий.
Примерно также при ФП дизайн получается не такой же как при ООП.
G>>Ну тогда будет много буферов и лупов, которые плохо скажутся на производительности. Кроме того фиксированная частота работает в среднем плохо, потому что частоты входящих потоков не фиксированы и варьируются в очень широком диапазоне. КЛ>Не думаю, что обычное приложение с тремя-пятью потоками, выполняемое под Windows на процессоре с 2-4 ядрами как-то резко скажется на производительности. Соответственно, буферов и лупов тоже будет немного — по числу потоков.
Ну мы ведь не об обычных приложениях говорим. Когда начинают возится с асинхронным IO, то рассчитывают на большую нагрузку. И тС привел пример приложений, в которых время реакции очень важно.
Re[16]: Good practice по алгоритмизации в условиях асинхронн
Что в приведенном псевдокоде Вам непонятно?
КЛ>>Я писал о внешних источниках — не о внутренних. G>А чем они хуже\лучше внутренних?
Тем, что они доступны в любой момент — под них не надо подстраиваться.
КЛ>>>>(1) либо запустить Принимальщик Решений в отдельном потоке со своей частотой, которая не зависит от частоты поступления событий; G>>>Плохо, потому что частоты входящих событий не фиксированы. КЛ>>Чем плохо? G>Тем что в среднем неэффективно получится, или реакция будет запаздывать, или ресурсы прожигаться будут. Так или иначе масштабируемость ухудшится.
Не думаю. Наоборот, основной цикл программы будет работать с той частотой, которая нам будет нужна, и не будет зависеть от перепадов нагрузки. Поток не зависнет, если по какой-либо причине количество входящих запросов вдруг резко возрастет. В целом, решение получается достаточно надежным и масштабируемым.
КЛ>>Понимаю, что это может сократить код при реализации, но не понимаю, как это влияет на проектирование и на выработку концептуального решения? G>Потому что вводится новая абстракция (первоклассная сущность) — поток событий. G>Примерно также при ФП дизайн получается не такой же как при ООП.
Не вижу причин, по которым "поток событий" нельзя описать без привязки с определенной технологии.
Абстрактно: Есть события А, Б и В, которые приходят с разной частотой.
Поток событий делается так: При поступлении каждое событие запоминается в некотором буфере. Далее — если пришло событие А, то берем Б и В из буфера и передаем на обработку. Если пришло Б, то берем А и В и т.д.
Технология тут не при чем. Фактически, получается то же самое, что я и описал при помощи таблиц.
G>Ну мы ведь не об обычных приложениях говорим. Когда начинают возится с асинхронным IO, то рассчитывают на большую нагрузку. И тС привел пример приложений, в которых время реакции очень важно.
На мой взгляд, лучше придерживаться поставленной задачи — иначе уйдем в дебри.
Понятно все, непонятно как это заставить работать эффективно. Нужен язык вроде Erlang с "зелеными" потоками, в других языках придется "зелень" вручную создавать.
КЛ>>>Я писал о внешних источниках — не о внутренних. G>>А чем они хуже\лучше внутренних? КЛ>Тем, что они доступны в любой момент — под них не надо подстраиваться.
Проблемы есть пока считаешь эти потоки разными, как только появляется абстракция, которая позволяет не "подстраиваться" сразу проблемы исчезают.
КЛ>>>>>(1) либо запустить Принимальщик Решений в отдельном потоке со своей частотой, которая не зависит от частоты поступления событий; G>>>>Плохо, потому что частоты входящих событий не фиксированы. КЛ>>>Чем плохо? G>>Тем что в среднем неэффективно получится, или реакция будет запаздывать, или ресурсы прожигаться будут. Так или иначе масштабируемость ухудшится. КЛ>Не думаю. Наоборот, основной цикл программы будет работать с той частотой, которая нам будет нужна, и не будет зависеть от перепадов нагрузки.
Не понял, выделенное за счет чего будет достигаться? Куда дополнительная нагрузка рассеиваться будет?
КЛ>Поток не зависнет, если по какой-либо причине количество входящих запросов вдруг резко возрастет. В целом, решение получается достаточно надежным и масштабируемым.
Ок, топикстартер приводил здесь код, я его написал в варианте с Rx. Напиши его в своем варианте, просто сравним результаты под нагрузкой.
КЛ>>>Понимаю, что это может сократить код при реализации, но не понимаю, как это влияет на проектирование и на выработку концептуального решения? G>>Потому что вводится новая абстракция (первоклассная сущность) — поток событий. G>>Примерно также при ФП дизайн получается не такой же как при ООП. КЛ>Не вижу причин, по которым "поток событий" нельзя описать без привязки с определенной технологии.
Таких причин собственно и нет. Rx уже портировали на JS и собираются портировать на С++. Сами интерфейсы, описывающие абстракции потоков событий, очень простые. Буквально два интерфейса с 1 и 3 методами. Сила Rx не столько в количестве написанного кода, сколько в том что этот код опирается на строгую математику.
КЛ>Абстрактно: Есть события А, Б и В, которые приходят с разной частотой. КЛ>Поток событий делается так: При поступлении каждое событие запоминается в некотором буфере. Далее — если пришло событие А, то берем Б и В из буфера и передаем на обработку. Если пришло Б, то берем А и В и т.д.
Меня собственно интересует реализация этой логики "если.., то.." не получился ли куча ифов, от которых старались уйти?
КЛ>Технология тут не при чем. Фактически, получается то же самое, что я и описал при помощи таблиц.
Именно технология причем. Цель известна, ТС привел код. Проблемы также известны — необходиомсть хранить состояние. Я варианты реализации уже привел, их буквально 3 штуки. Теперь можем попробовать на реальном коде оценить достоинства и недостатки каждого из них.
G>>Ну мы ведь не об обычных приложениях говорим. Когда начинают возится с асинхронным IO, то рассчитывают на большую нагрузку. И тС привел пример приложений, в которых время реакции очень важно. КЛ>На мой взгляд, лучше придерживаться поставленной задачи — иначе уйдем в дебри.
Ну так поставленная задача — написать торгового бота, обычно серьезные торговые стратегии опрерируют кучей событий и работают под большой нагрузкой, поэтому нужна масштабируемость (то есть минимальный порядок роста потребляемых ресурсов при возрастании количества входящих сообщений).
Re[18]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
КЛ>>Что в приведенном псевдокоде Вам непонятно? G>Понятно все, непонятно как это заставить работать эффективно. Нужен язык вроде Erlang с "зелеными" потоками, в других языках придется "зелень" вручную создавать.
Трудно комментировать оценки. По приведенному коду есть какой-нибудь осмысленный вопрос?
КЛ>>Не думаю. Наоборот, основной цикл программы будет работать с той частотой, которая нам будет нужна, и не будет зависеть от перепадов нагрузки. G>Не понял, выделенное за счет чего будет достигаться? Куда дополнительная нагрузка рассеиваться будет?
За счет того, что основной цикл будет зависеть от частоты поступающих сообщений. А дополнительная нагрузка будет "рассеиваться":
(1) либо сама собой, когда минует пиковая нагрузка;
(2) либо при помощи потери устаревших сообщений;
(3) либо путем распределения нагрузки между доп. серверами.
В последнем случае у нас будет не один обработчик бизнес-логики, а несколько.
КЛ>>Поток не зависнет, если по какой-либо причине количество входящих запросов вдруг резко возрастет. В целом, решение получается достаточно надежным и масштабируемым. G>Ок, топикстартер приводил здесь код, я его написал в варианте с Rx. Напиши его в своем варианте, просто сравним результаты под нагрузкой.
У меня нет особого желания переубеждать тебя в чем-то. Высказать свои аргументы я могу, а вот тратить несколько часов (а может и дней) своего времени на написание полностью работающего и отлаженного кода на С++ у меня, честно говоря, нет желания. Но если ты все-таки хочешь убедиться в том, чей вариант будет работать лучше, то предлагаю тебе реализовать оба варианта (и свой, и мой) самому, самостоятельно провести тестирование и сообщить нам о результатах. И мне, и коллегам, полагаю, это будет интересно.
КЛ>>Абстрактно: Есть события А, Б и В, которые приходят с разной частотой. КЛ>>Поток событий делается так: При поступлении каждое событие запоминается в некотором буфере. Далее — если пришло событие А, то берем Б и В из буфера и передаем на обработку. Если пришло Б, то берем А и В и т.д. G>Меня собственно интересует реализация этой логики "если.., то.." не получился ли куча ифов, от которых старались уйти?
Нет, не получится. По крайней мере, не будет вложенности условий, и код будет содержать один оператор if.
КЛ>>Технология тут не при чем. Фактически, получается то же самое, что я и описал при помощи таблиц. G>Именно технология причем. Цель известна, ТС привел код. Проблемы также известны — необходиомсть хранить состояние. Я варианты реализации уже привел, их буквально 3 штуки. Теперь можем попробовать на реальном коде оценить достоинства и недостатки каждого из них.
Технологии постоянно меняются + зависят от платформы и языка. А решая задачу без привязки к технологиям, можно очень легко переносить решения с платформы на платформу. Фактически, благодаря такому подходу мне и моим бывшим коллегам было нетрудно портировать навигационную систему с платформы на платформу, например, с Windows Mobile под Symbian.
КЛ>>На мой взгляд, лучше придерживаться поставленной задачи — иначе уйдем в дебри. G>Ну так поставленная задача — написать торгового бота, обычно серьезные торговые стратегии опрерируют кучей событий и работают под большой нагрузкой, поэтому нужна масштабируемость (то есть минимальный порядок роста потребляемых ресурсов при возрастании количества входящих сообщений).
Это уже твое предположение (пусть и имеющее некоторые основания). В исходной постановке об этом не было ни слова.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, gandjustas, Вы писали:
КЛ>>>Что в приведенном псевдокоде Вам непонятно? G>>Понятно все, непонятно как это заставить работать эффективно. Нужен язык вроде Erlang с "зелеными" потоками, в других языках придется "зелень" вручную создавать.
КЛ>Трудно комментировать оценки. По приведенному коду есть какой-нибудь осмысленный вопрос?
КЛ>>>Не думаю. Наоборот, основной цикл программы будет работать с той частотой, которая нам будет нужна, и не будет зависеть от перепадов нагрузки. G>>Не понял, выделенное за счет чего будет достигаться? Куда дополнительная нагрузка рассеиваться будет? КЛ>За счет того, что основной цикл будет зависеть от частоты поступающих сообщений. А дополнительная нагрузка будет "рассеиваться":
КЛ>(1) либо сама собой, когда минует пиковая нагрузка;
Она не минутет. Для торговых систем она в среднем стабильна.
КЛ>(2) либо при помощи потери устаревших сообщений;
Такое вообще категорически нельзя делать. Торговые системы близки к реалтайму, большинство сообщений должны быть обработаны.
КЛ>>>Поток не зависнет, если по какой-либо причине количество входящих запросов вдруг резко возрастет. В целом, решение получается достаточно надежным и масштабируемым. G>>Ок, топикстартер приводил здесь код, я его написал в варианте с Rx. Напиши его в своем варианте, просто сравним результаты под нагрузкой.
КЛ>У меня нет особого желания переубеждать тебя в чем-то. Высказать свои аргументы я могу, а вот тратить несколько часов (а может и дней) своего времени на написание полностью работающего и отлаженного кода на С++ у меня, честно говоря, нет желания. Но если ты все-таки хочешь убедиться в том, чей вариант будет работать лучше, то предлагаю тебе реализовать оба варианта (и свой, и мой) самому, самостоятельно провести тестирование и сообщить нам о результатах. И мне, и коллегам, полагаю, это будет интересно.
ТС привел код, я приводил свой варинат. Писать агенты у меня нет желания.
Кроме того я до сих пор не понимаю как будут обрабатываться события и как о них будет узнавать обработчик БЛ.
КЛ>>>Абстрактно: Есть события А, Б и В, которые приходят с разной частотой. КЛ>>>Поток событий делается так: При поступлении каждое событие запоминается в некотором буфере. Далее — если пришло событие А, то берем Б и В из буфера и передаем на обработку. Если пришло Б, то берем А и В и т.д. G>>Меня собственно интересует реализация этой логики "если.., то.." не получился ли куча ифов, от которых старались уйти? КЛ>Нет, не получится. По крайней мере, не будет вложенности условий, и код будет содержать один оператор if.
Те псевдокод такой
Если пришло А
{
Взять Б.
В зависимости от (А,Б) выполнить действие (тоже if)
}
Если пришло Б
{
Взять А.
В зависимости от (А,Б) выполнить действие (тоже if).
}
В итоге два ифа, как и у ТС. Также получится два ифа если делать явный автомат.
Что выигрываем?
КЛ>>>Технология тут не при чем. Фактически, получается то же самое, что я и описал при помощи таблиц. G>>Именно технология причем. Цель известна, ТС привел код. Проблемы также известны — необходиомсть хранить состояние. Я варианты реализации уже привел, их буквально 3 штуки. Теперь можем попробовать на реальном коде оценить достоинства и недостатки каждого из них.
КЛ>Технологии постоянно меняются + зависят от платформы и языка. А решая задачу без привязки к технологиям, можно очень легко переносить решения с платформы на платформу. Фактически, благодаря такому подходу мне и моим бывшим коллегам было нетрудно портировать навигационную систему с платформы на платформу, например, с Windows Mobile под Symbian.
Решение всегда зависит от языка и технологий. Например код на функциональном языке всегда не поход на код на императивном языке.
Более частный пример: отсутствие yield в C++ не позволит эффективно писать итератор, который позволяет выполнять линейный код работать асинхронно.
Другой частный пример: в Silverlight\js все взаимодействие с внешним миром асинхронно, поэтому любителям писать синхронный код надо напрягаться с передачей контекста от места вызова до места обработки.
Всегда можно проектировать абстрактное решение абстрактной задачи, но в итоге в коде оно обычно получается не такое как хотелось. Я вот например до сих пор не пойму как будет делаться обработка сообщений в твоем варианте.
КЛ>>>На мой взгляд, лучше придерживаться поставленной задачи — иначе уйдем в дебри. G>>Ну так поставленная задача — написать торгового бота, обычно серьезные торговые стратегии опрерируют кучей событий и работают под большой нагрузкой, поэтому нужна масштабируемость (то есть минимальный порядок роста потребляемых ресурсов при возрастании количества входящих сообщений). КЛ>Это уже твое предположение (пусть и имеющее некоторые основания). В исходной постановке об этом не было ни слова.
Дык топикстартер привел конкретный код.
Re[20]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, gandjustas, Вы писали:
КЛ>>(1) либо сама собой, когда минует пиковая нагрузка; G>Она не минутет. Для торговых систем она в среднем стабильна.
Тогда и не будет проблем с производительностью.
G>ТС привел код, я приводил свой варинат. Писать агенты у меня нет желания. G>Кроме того я до сих пор не понимаю как будут обрабатываться события и как о них будет узнавать обработчик БЛ.
Обработчик БЛ берет данные из таблиц (их описание см. здесь
). Он может "тупо" раз в определенное время "просыпаться", выполнять БЛ, а затем — "засыпать". Но можно его "пробуждение" приурочивать и к биржевому тику, как я писал выше.
Собственно говоря, об этом я уже писал выше не один раз.
G>Те псевдокод такой
G>
G>Если пришло А
G>{
G> Взять Б.
G> В зависимости от (А,Б) выполнить действие (тоже if)
G>}
G>Если пришло Б
G>{
G> Взять А.
G> В зависимости от (А,Б) выполнить действие (тоже if).
G>}
G>
G>В итоге два ифа, как и у ТС. Также получится два ифа если делать явный автомат. G>Что выигрываем?
if (A)
{
if (B)
{
if (!C)
do1();
do2();
}
else
{
if (C)
do1();
else
do3();
do4();
}
}
else
{
if (B)
{
if (!C)
do1();
do5();
}
else
{
if (C)
do1();
else
do6();
do2();
}
}
Чтобы избавиться от ветвистости, нужно выполнить такой алгоритм:
Шаг 1.Сгруппировать условия по действиям.
Т.е. к каждой функции приписать условия, при которых она выполняется, индивидуально.
Получится:
if (A && B && !C || A && !B && C || !A && B && !C || !A && !B && C)
do1();
if (!A && !B && !C)
do6();
if (A && B || !A && !B)
do2();
if (A && !B && !C)
do3();
if (A && !B)
do4();
if (!A && B)
do5();
Шаг 2. Упростить логические выражения.
Получим:
if (B != С)
do1();
if (!A && !B && !C)
do6();
if (A == B)
do2();
if (A && !B && !C)
do3();
if (A && !B)
do4();
if (!A && B)
do5();
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, gandjustas, Вы писали:
КЛ>>>(1) либо сама собой, когда минует пиковая нагрузка; G>>Она не минутет. Для торговых систем она в среднем стабильна. КЛ>Тогда и не будет проблем с производительностью.
Проблемы с производительность для торговых систем тоже стабильны
G>>ТС привел код, я приводил свой варинат. Писать агенты у меня нет желания. G>>Кроме того я до сих пор не понимаю как будут обрабатываться события и как о них будет узнавать обработчик БЛ.
КЛ>Обработчик БЛ берет данные из таблиц (их описание см. здесь
). Он может "тупо" раз в определенное время "просыпаться", выполнять БЛ, а затем — "засыпать". Но можно его "пробуждение" приурочивать и к биржевому тику, как я писал выше. КЛ>Собственно говоря, об этом я уже писал выше не один раз.
Ага, а теперь сами события. Как они будут попадать в таблицы? (как понимаю это будут очереди на самом деле, терять то нельзя).
G>>Те псевдокод такой
G>>
G>>Если пришло А
G>>{
G>> Взять Б.
G>> В зависимости от (А,Б) выполнить действие (тоже if)
G>>}
G>>Если пришло Б
G>>{
G>> Взять А.
G>> В зависимости от (А,Б) выполнить действие (тоже if).
G>>}
G>>
G>>В итоге два ифа, как и у ТС. Также получится два ифа если делать явный автомат. G>>Что выигрываем?
КЛ>Нет, код будет совсем другой. Выше
КЛ>Чтобы избавиться от ветвистости, нужно выполнить такой алгоритм:
КЛ>Шаг 1.Сгруппировать условия по действиям. КЛ>Т.е. к каждой функции приписать условия, при которых она выполняется, индивидуально.
КЛ>Получится:
КЛ>
КЛ>if (A && B && !C || A && !B && C || !A && B && !C || !A && !B && C)
КЛ> do1();
КЛ>if (!A && !B && !C)
КЛ> do6();
КЛ>if (A && B || !A && !B)
КЛ> do2();
КЛ>if (A && !B && !C)
КЛ> do3();
КЛ>if (A && !B)
КЛ> do4();
КЛ>if (!A && B)
КЛ> do5();
КЛ>
КЛ>if (B != С)
КЛ> do1();
КЛ>if (!A && !B && !C)
КЛ> do6();
КЛ>if (A == B)
КЛ> do2();
КЛ>if (A && !B && !C)
КЛ> do3();
КЛ>if (A && !B)
КЛ> do4();
КЛ>if (!A && B)
КЛ> do5();
КЛ>
КЛ>Теперь стало понятнее?
Конечно, это чисто механическое действие, но ТС говорил вообще-то о другом. Я немного знаком с асинхронным программированием и знаю как громоздко выглядит нелинейный асинхронный код. Тем не менее все сводится к автомату, который реализуется ифами двух уровней (а может и одним — селектором верхнего уровня служит само событие).
А вот выкрутить в обратную сторону, как ты предлагаешь, довольно проблематично. Условие "наступило событие А текущее состояние 1 или наступило событие Б и текущее событие 2" простым способом не запишешь. Потому что событие — это push, а проверка в ифе — pull. Вот и приходится платить довольно высокую цену — создавать очереди, плодить потоки итп, только чтобы из полутора вложенных ифов получить один. Это не только хуже работает, но и сложнее сопровождается.
Re[29]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>Что значит асинхронно? В смысле в другом потоке? Ну так поставь таску на чтение файла в другой поток, а не в основной и будет тебе счастье.
налицо трагическое непонимание. Нет никакого "другого потока". В IOCP чтение выполняет не поток, а, грубо говоря, контроллер ввода-вывода. Именно в этом принципиальная разница. Тратить мегабайт стека только на то, чтобы он спал в ожидании контроллера — бессмысленное расточительство. С биржей всё будет ещё хуже: скажем, три тысячи отслеживаемых тикеров просто не влезут в 32хразрядное адресное пространство, если каждому выделять свой поток. Не говоря уже о том, что накладные расходы на пробуждение такого потока тоже весьма отличны от нуля.
Код, построенный на основе IOCP, может одновременно ждать сотни тысяч тикеров без заметного роста нагрузки на CPU и память. А это важно — меньше шансов попасть в своп, из которого долго подниматься при приходе долгожданного события.
При этом собственно время обработки события пренебрежимо мало — процессору достаточно наносекунд, чтобы выполнить все вычисления и принять торговое решение.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[31]: Good practice по алгоритмизации в условиях асинхронн
Здравствуйте, Undying, Вы писали:
U>А асинхронная операция в ОС она магическая или как? Ни потоков, ни процессорного времени не использует?
Совершенно верно. Она магическая. Зачем, к примеру, тратить процессорное время и/или потоки на ожидание приезда пакета в сетевую карту? Там внутри достаточно оборудования, чтобы всё сделать без нас.
Вот когда данные приехали, можно и сообщить в пользовательский код, что пора за работу. Именно так и работают современные высокопроизводительные приложения.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Good practice по алгоритмизации в условиях асинхронно
Бороться со сложностью можно используя иерархические КА, в которых состояние само может быть КА.
Но "надрыв мозга" при составлении КА конечно же будет.
Кстати исходный "линейный" алгоритм (из того разряда, который я приводил в примере) отчасти так или иначе в явном виде содержит КА, поскольку состоит из:
1. Описаний последовательностей команд и проверяемых условий при приходе тех или иных событий
2. Описание своего рода "синхронизации" действий, т.е. что делать если в момент прихода события, на которое нужно реагировать, уже выполняется какое-то действие..
Честно говоря, по п.2 не вижу более естественного и популярного пути описания этой логики, чем КА.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, asimonenko, Вы писали:
A>>Все равно все сведется к автомату, ИМХО. Т.е. данные появляются асинхронно, и при определенных условиях (наборе данных) ты должен выполнить определенное действие.
A>>То же самое с сериализацией — при десериализации ты логично "двигаешь" автомат в нужную точку последовательностью входящих данных.
A>>Словом, КА — просто и надежно. Только его нужно сразу выявить, формализовать (куда-нибудь записать диаграммку) и реализовать на ранних стадиях, пока не наворочена неформализованная куча -)
G>Для автоматов нет способов борьбы со сложностью. Если состояний станет слишком много, то не не будет возможности как-то исправить.
Re[4]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, sergunok, Вы писали:
S>Бороться со сложностью можно используя иерархические КА, в которых состояние само может быть КА.
S>Но "надрыв мозга" при составлении КА конечно же будет.
S>Кстати исходный "линейный" алгоритм (из того разряда, который я приводил в примере) отчасти так или иначе в явном виде содержит КА, поскольку состоит из: S>1. Описаний последовательностей команд и проверяемых условий при приходе тех или иных событий S>2. Описание своего рода "синхронизации" действий, т.е. что делать если в момент прихода события, на которое нужно реагировать, уже выполняется какое-то действие..
S>Честно говоря, по п.2 не вижу более естественного и популярного пути описания этой логики, чем КА.
Это теория, на практике автомат с нетривиильной логикой в котором более 10 состояний и от каждого более одного перехода — получается страшный макаронный код, который очень тяжело поддерживать. Все потому что нету у КА средств борьбы со сложностьюю
Re[3]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Aviator, Вы писали:
A>Здравствуйте, gandjustas, Вы писали:
G>>Смотри Rx A>Давно интересовался практическими применениями этой либы, может есть у кого случаи успешного применения в рабочих проектах?
Мне очень туго пришлось бы в последнем проекте без этой штуки. Особенно выручает в ситуациях, когда возникает необходимость отправить сервису команду А, асинхронно получить результат, потом на его сформировать команду Б ..., потом проделать тоже самое со следующим сервисом, и так далее, и так далее, а если в процессе произойдет ошибка, то послать первому сервису команду ...
Re[4]: Good practice по алгоритмизации в условиях асинхронно
Здравствуйте, Lazin, Вы писали:
L>Здравствуйте, Aviator, Вы писали:
A>>Здравствуйте, gandjustas, Вы писали:
G>>>Смотри Rx A>>Давно интересовался практическими применениями этой либы, может есть у кого случаи успешного применения в рабочих проектах?
L>Мне очень туго пришлось бы в последнем проекте без этой штуки. Особенно выручает в ситуациях, когда возникает необходимость отправить сервису команду А, асинхронно получить результат, потом на его сформировать команду Б ..., потом проделать тоже самое со следующим сервисом, и так далее, и так далее, а если в процессе произойдет ошибка, то послать первому сервису команду ...
Может быть приведешь примерчик кода того применения, которое порадоволо?
Re[5]: Good practice по алгоритмизации в условиях асинхронно
порадовало легкостью, с которой некая библиотека доступа к данным, не следующая общепринятым паттернам, может быть использована вместе с Rx. Здесь выполняется подписка на изменение данных, поступающих из некоего хранилища, query — запрос на подписку, OnCode — вызывается при возникновении разных ошибок, OnData — при появлении новых данных, Start — выполняет запрос, End — отменяет подписку. Все это безобразие приводится к интерфейсу IObservable с минимальными усилиями, без необходимости написания кучи boilerplate кода.
Но больше всего радует конечно же возможность комбинировать разные IObservable объекты.
Вот такой код, к примеру, у меня отвечает за отображение текущего состояния некоего объекта:
Если бы я не использовал Rx, то пришлось бы создавать объект — конечный автомат, который бы в ответ на события изменял свое состояние и сам был бы источником событий для пользовательского интерфейса. А здесь — все в одном месте, никакого явного состояния, хотя конечно, детали того как происходит подписка на те или иные события, спрятана в ф-ях SuiteXXX, но эти детали и не нужны коду, отвечающему за пользовательский интерфейс