Геннадий Ванин (Gennady Vanin)
|
Date: 1/11/2013
|
|
|
|
FlashPlayer Code
private readonly SynchronizedDictionary<Security, QuotesWindow> _quotesWindows =
new SynchronizedDictionary<Security, QuotesWindow>();
private readonly SynchronizedDictionary<MarketDepth, QuotesWindow> _changedDepths =
new SynchronizedDictionary<MarketDepth, QuotesWindow>();
Просто я думал логика должна была быть примерно такая - достаточно держать лишь один словарь - _quotesWindows и при получении обновленного MarketDepth - найти соответствующий ему элемент в _quotesWindows и обновить _quotesWindows.Value. Разве не так? Или другими словами можно было бы исключить Code
private readonly SynchronizedDictionary<Security, QuotesWindow> _quotesWindows = new SynchronizedDictionary<Security, QuotesWindow>();
т.к. между экземпляром инструмента (Security) и экземпляром стакана (MarketDepth) существует однозначная связь, а security - экземпляр Security определяется, как из экземпляра стакана depth (MarketDepth), так и экз-ра котировок стакана, привязанного к окну QuotesWindow (ObservableCollection<Quote>) Но, в наст. реализации, _quotesWindows - это экземпляр окна котировок, содержащее коллекцию котировок, не содержащие внутри себя идентификацию инструмента (стакана) или стакана (инструмента) этих котировок: Code
public partial class QuotesWindow
{
public QuotesWindow()
{
Quotes = new ObservableCollection<Quote>();
InitializeComponent();
}
public ObservableCollection<Quote> Quotes { get; private set; }
}
Зато для этого есть словари, из которых по экземпляру инструментов (Security) или стаканов (MarketDepth) можно извлечь экземпляр коллекции котировок - QuotesWindow, т.е. ObservableCollection<Quote> Насколько я понимаю, есть другие необходимости - обычно для одного и того же инструмента (в терминалах и программах S#) можно открывать несколько окон одного и того же стакана, для каждого со своим клоном-копией (данных) стакана.
В общем, неочевидно, что нужны твкие усложнения, при том, что теряется гибкость и структурность + см. следующее соображение
- В рассматриваемом приложении, данные инструментов копируются-дублируются во много окон
- SecuritiesWindows, котировки-стакана (каждого инструмента) QuotesWindow и др. (MyTradesWindows, OrdersWindow, PositionsWindows и др.) . показываются в разных окнах с копиями данных, некоторые из которых задублированные копии одних и тех же сущностей (например - инструмент) Поэтому делать из экземпляров объектов инструмента синглтоны - неоправданно усложнять, при этом теряя в гибкости
FlashPlayer Code
private readonly SynchronizedDictionary<Security, QuotesWindow> _quotesWindows =
new SynchronizedDictionary<Security, QuotesWindow>();
private readonly SynchronizedDictionary<MarketDepth, QuotesWindow> _changedDepths =
new SynchronizedDictionary<MarketDepth, QuotesWindow>();
Далее происходит подписка на обновление стаканов trader.MarketDepthsChanged += TraderOnMarketDepthsChanged, и вот совсем мне непонятно, что делается в TraderOnMarketDepthsChanged: Code
private void TraderOnMarketDepthsChanged(IEnumerable<MarketDepth> depths)
{
foreach (var depth in depths)
{
var wnd = _quotesWindows.TryGetValue(depth.Security);
if (wnd != null)
_changedDepths[depth] = wnd;
}
}
Что тут происходит, как я понимаю: рассматриваем каждый обновившийся стакан и ищем соответствующее ему окно стакана в _quotesWindows: var wnd = _quotesWindows.TryGetValue(depth.Security); Насколько я понимаю, котировки дублируются в 2х словарях и при их обновлении их надо скопировать (синхронизировать) из одного в другой FlashPlayer Далее что происходит в этой строчке: _changedDepths[depth] = wnd; ? Откуда мы знаем, что в словаре _changedDepths есть элемент с ключом depth? Ну, так они для того и называются синхронизированными Их вначале инициализируют "одинаково" (т.е. синхронизованно) по (структурам данных) выбранным инструментов (каждой из которых соответствует стакан, вернее структура данных стакана), обновления котрых нас интересуют, а потом их танцуют Детали можно посмотреть с помощью ReSharper
|
|
Thanks:
|
|
|
|
|
FlashPlayer
|
Date: 1/11/2013
|
|
|
|
Огромное спасибо за такой развернутый ответ. Все практически встало на свои места. Остался один вопрос по теме - как все таки работает "синхронизация" мд этими двумя словарями? Вот мы их проинициализировали - два словаря с одинаковыми значениями, но разными ключами. Неужели этого достаточно для того, чтобы если в одном словаре появился новый элемент, то он появился и в другом? Я честно гуглил,но так и не понял как работает этот класс. И есть еще один вопрос, который связан с моими непониманием некоторых особенностей языка. Помогите пожалуйста также наглядно разобраться с объектом класса GuiDispatcher : _dispatcher. Вот в том же примере в том же классе SecuritiesWindow встречается такой код: Code
_dispatcher.AddPeriodicalAction(() =>
{
foreach (var pair in _changedDepths.SyncGet(s => s.CopyAndClear()))
{
pair.Value.Quotes.Clear();
pair.Value.Quotes.AddRange(pair.Key.SyncGet(md => md.Clone().Reverse()));
}
});
Как я понимаю, что тут происходит: AddPeriodicalAction, как следует из названия, вызывает с какой-то (с какой???) периодичностью нижеидущее действие, которое, в свою очередь, заполняет QuotesWindow из MarketDepth. Как же все таки истинно работает этот диспатчер - в чем его обязанность, удобство перед таймером или вообще необходимость? Ведь теперь получается совершенно запутанная последовательность действия, как я понимаю. Вот у нас есть два словаря, пока что пустых. Пользователь кликает по кнопке создать стакан и в словарь _quotesWindows добавляется элемент А = (Security, QuotesWindow). По идее, следуя из того, что словари синхронизированы (хотя я до сих пор не понимаю, как это осуществляется, где и кем) в словаре _changedDepths появляется элемент В = (MarketDepth, QuotesWindow). Причем А и В имеют одно и тоже значение,а ключи разные, хотя и соответствуют одному и тому же инструменту. Далее в TraderOnMarketDepthsChanged по каждому обновленному MarketDepth делается что-то невероятное: ищется соответствующий ему элемент в _quotesWindows и соответствующий ему элемент в _changedDepths, после этого QuotesWindow из первого присваивается второму. Зачем?!Вот этот момент мне не понятен. Далее, как раз с помощью диспатчера (что мне тоже непонятно, но выше я уже это спросил) в _changedDepths во всех элементах берутся котировки из ключа и пихаются в значение. Таким образом мы наконец-то получаем обновленный QuotesWindow (то есть в стакане нашем, наконец-то появляются обновленные котировки). Но это же адово сложный процесс. Ведь можно, как вы сказали выше, вести только один словарь _changedDepths, и в событии TraderOnMarketDepthsChanged делать то, что делается в _dispatcher.AddPeriodicalAction и результат по идее дб тот же. Или нет? Спасибо заранее за пояснения. А то оч уж сложная механика, для вроде кажущихся несложный действий.
|
|
Thanks:
|
|
|
|
|
FlashPlayer
|
Date: 1/11/2013
Геннадий Ванин (Gennady Vanin) [quote=FlashPlayer;23289]
Насколько я понимаю, котировки дублируются в 2х словарях и при их обновлении их надо скопировать (синхронизировать) из одного в другой
Вот тут тоже спорно и непонятно, ведь именно в TraderOnMarketDepthsChanged никакого обновления ни одной котировки не происходит - тут то и беда. Оно происходит только в диспатчере этом. И вот такая запутанность непонятна. В TraderOnMarketDepthsChanged только копирование по ссылке значений происходит (опять же непонятно зачем). Короче совсем каша какая-то.
|
|
Thanks:
|
|
|
|
|
Moadip
|
Date: 1/11/2013
|
|
|
|
Quote:Как я понимаю, что тут происходит: AddPeriodicalAction, как следует из названия, вызывает с какой-то (с какой???) периодичностью нижеидущее действие, которое, в свою очередь, заполняет QuotesWindow из MarketDepth. Как же все таки истинно работает этот диспатчер - в чем его обязанность, удобство перед таймером или вообще необходимость? Библиотека Ecng.Xaml не обфусцирована. И когда возникают вопросы как же оно там все внутри устроено, можно открыть и посмотреть код Reflector или R#. Как бесплатная альтернатива dotPeek. Открываем, смотрим: Code
public void AddPeriodicalAction(Action action)
{
if (action == null)
throw new ArgumentNullException("action");
this._periodicalActions.Add(action);
this.StartTimer();
}
Переходим по StartTimer: Code
private void StartTimer()
{
this._lastTime = DateTime.Now;
lock (this._lock)
{
if (this._timer != null)
return;
this._timer = new DispatcherTimer(DispatcherPriority.Normal, this.Dispatcher);
this._timer.Tick += new EventHandler(this.OnTimerTick);
this._timer.Interval = new TimeSpan(this.Interval.Ticks / 10L);
this._timer.Start();
}
}
Видим создание объекта типа DispatcherTimer. Полное имя - System.Windows.Threading.DispatcherTimer, что это за класс можно почитать на MSDN. Дальше интересна строчка: Code
this._timer.Interval = new TimeSpan(this.Interval.Ticks / 10L);
Точнее this.Interval.Ticks. Переходим по Interval и видим следующее: Code
public TimeSpan Interval
{
get
{
return this._interval;
}
set
{
if (value <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException("value");
this._interval = value;
this.StopTimer();
this.StartTimer();
}
}
И последний переход по _ interval, в то место где данное поле инициализируется: Code
private TimeSpan _interval = TimeSpan.FromMilliseconds(100.0);
В итоге по умолчания AddPeriodicalAction работает с периодичностью = TimeSpan.FromMilliseconds(100.0).Ticks / 10L.
|
|
Thanks:
|
|
|
|
|
FlashPlayer
|
Date: 1/12/2013
|
|
|
|
Хм, спасибо снова за такие пояснения. Не все понятно в середине, но важен итог - получается AddPeriodicalAction вызывается каждые 10мс? Тогда остается открытой эта часть моего вопроса:
" Ведь теперь получается совершенно запутанная последовательность действия, как я понимаю. Вот у нас есть два словаря, пока что пустых. Пользователь кликает по кнопке создать стакан и в словарь _quotesWindows добавляется элемент А = (Security, QuotesWindow). По идее, следуя из того, что словари синхронизированы (хотя я до сих пор не понимаю, как это осуществляется, где и кем) в словаре _changedDepths появляется элемент В = (MarketDepth, QuotesWindow). Причем А и В имеют одно и тоже значение,а ключи разные, хотя и соответствуют одному и тому же инструменту. Далее в TraderOnMarketDepthsChanged по каждому обновленному MarketDepth делается что-то невероятное: ищется соответствующий ему элемент в _quotesWindows и соответствующий ему элемент в _changedDepths, после этого QuotesWindow из первого присваивается второму. Зачем?!Вот этот момент мне не понятен. Далее, как раз с помощью диспатчера (что мне тоже непонятно, но выше я уже это спросил) в _changedDepths во всех элементах берутся котировки из ключа и пихаются в значение. Таким образом мы наконец-то получаем обновленный QuotesWindow (то есть в стакане нашем, наконец-то появляются обновленные котировки). Но это же адово сложный процесс.
Ведь можно, как вы сказали выше, вести только один словарь _changedDepths, и в событии TraderOnMarketDepthsChanged делать то, что делается в _dispatcher.AddPeriodicalAction и результат по идее дб тот же. Или нет? Спасибо заранее за пояснения. А то оч уж сложная механика, для вроде кажущихся несложный действий. "
И к тому же добавляется еще один - а не накладно каждые 10мс вызывать обновление всего списка котировок, вместо того, чтобы обновлять его по событию?
|
|
Thanks:
|
|
|
|
|
Геннадий Ванин (Gennady Vanin)
|
Date: 1/12/2013
|
|
|
|
FlashPlayer Ведь теперь получается совершенно запутанная последовательность действия, как я понимаю. Вот у нас есть два словаря, пока что пустых. Пользователь кликает по кнопке создать стакан и в словарь _quotesWindows добавляется элемент А = (Security, QuotesWindow). По идее, следуя из того, что словари синхронизированы (хотя я до сих пор не понимаю, как это осуществляется, где и кем) в словаре _changedDepths появляется элемент В = (MarketDepth, QuotesWindow). Причем А и В имеют одно и тоже значение,а ключи разные, хотя и соответствуют одному и тому же инструменту. Ну, дак в соответствии с Вами же процитированным кодом: Code
private void TraderOnMarketDepthsChanged(IEnumerable<MarketDepth> depths)
{
foreach (var depth in depths)
{
var wnd = _quotesWindows.TryGetValue(depth.Security);
if (wnd != null)
_changedDepths[depth] = wnd;
}
}
FlashPlayer Далее в TraderOnMarketDepthsChanged по каждому обновленному MarketDepth делается что-то невероятное Это не далее, уже проехали выше FlashPlayer Ведь можно, как вы сказали выше, вести только один словарь _changedDepths и в событии TraderOnMarketDepthsChanged делать то, что делается в _dispatcher.AddPeriodicalAction и результат по идее дб тот же Делайте Если бы мне нужно было бы, я бы сделал
|
|
Thanks:
|
|
|
|
|
FlashPlayer
|
Date: 1/13/2013
Понятно, то есть одним словом в примере немного мудрено реализован механизм?
|
|
Thanks:
|
|
|
|