← Zurück

GuiDispatcher.AddPeriodicalAction

Подскажите пожалуйста - как работает GuiDispatcher.AddPeriodicalAction? Судя из названия метода - действие вызывается периодически - но с какой частотой. Пример использования встречается в SampleGui, где с помощью этого метода обновляется информация в открытых стаканах. Логика там, как я понимаю, следующая - ведется два синхронизированных словаря ключ-значение: один - инструмент/окно стакана, другой - рыночный стакан/окно стакана.

	private readonly SynchronizedDictionary<Security, QuotesWindow> _quotesWindows = new SynchronizedDictionary<Security, QuotesWindow>();
            private readonly SynchronizedDictionary<MarketDepth, QuotesWindow> _changedDepths = new SynchronizedDictionary<MarketDepth, QuotesWindow>(); 

Далее происходит подписка на обновление стаканов trader.MarketDepthsChanged += TraderOnMarketDepthsChanged, и вот совсем мне непонятно, что делается в TraderOnMarketDepthsChanged:

            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);

Далее что происходит в этой строчке: _changedDepths[depth] = wnd; ? Откуда мы знаем, что в словаре _changedDepths есть элемент с ключом depth? И зачем на каждом обновлении стакана depth мы переприсваиваем значение wnd в словаре? Ну и самый главные вопрос - как потом в wnd появляются новые котировки с обновленного depth? Как работает .AddPeriodicalAction?

Просто я думал логика должна была быть примерно такая - достаточно держать лишь один словарь - _quotesWindows и при получении обновленного MarketDepth - найти соответствующий ему элемент в _quotesWindows и обновить _quotesWindows.Value. Разве не так?

Я еще только начинаю разбираться в программировании такого уровня, так что сильно не бейте и спасибо за помощь.

War dieses Thema hilfreich?

Thema teilen

Kommentare (7)

Anmelden oder Konto erstellen, Melden Sie sich an oder registrieren Sie sich, um einen Kommentar zu hinterlassen

Геннадий Ванин (Gennady Vanin)

FlashPlayer: ...

Просто я думал логика должна была быть примерно такая - достаточно держать лишь один словарь - _quotesWindows и при получении обновленного MarketDepth - найти соответствующий ему элемент в _quotesWindows и обновить _quotesWindows.Value. Разве не так?

Или другими словами можно было бы исключить

private readonly SynchronizedDictionary<Security, QuotesWindow> _quotesWindows = new SynchronizedDictionary<Security, QuotesWindow>(); 

т.к. между экземпляром инструмента (Security) и экземпляром стакана (MarketDepth) существует однозначная связь, а security - экземпляр Security определяется, как из экземпляра стакана depth (MarketDepth), так и экз-ра котировок стакана, привязанного к окну QuotesWindow (ObservableCollection<Quote>)

Но, в наст. реализации, _quotesWindows - это экземпляр окна котировок, содержащее коллекцию котировок, не содержащие внутри себя идентификацию инструмента (стакана) или стакана (инструмента) этих котировок:


public partial class QuotesWindow
{
    public QuotesWindow()
    {
        Quotes = new ObservableCollection<Quote>();
	InitializeComponent();
     }

     public ObservableCollection<Quote> Quotes { get; private set; }
}  

QuotesWindow.xaml

Зато для этого есть словари, из которых по экземпляру инструментов (Security) или стаканов (MarketDepth) можно извлечь экземпляр коллекции котировок - QuotesWindow, т.е. ObservableCollection<Quote>

Насколько я понимаю, есть другие необходимости

  • обычно для одного и того же инструмента (в терминалах и программах S#) можно открывать несколько окон одного и того же стакана, для каждого со своим клоном-копией (данных) стакана.

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

  • В рассматриваемом приложении, данные инструментов копируются-дублируются во много окон
  • SecuritiesWindows, котировки-стакана (каждого инструмента) QuotesWindow и др. (MyTradesWindows, OrdersWindow, PositionsWindows и др.) . показываются в разных окнах с копиями данных, некоторые из которых задублированные копии одних и тех же сущностей (например - инструмент) Поэтому делать из экземпляров объектов инструмента синглтоны - неоправданно усложнять, при этом теряя в гибкости

FlashPlayer: ...

Далее происходит подписка на обновление стаканов trader.MarketDepthsChanged += TraderOnMarketDepthsChanged, и вот совсем мне непонятно, что делается в TraderOnMarketDepthsChanged:

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](@message(23289)):**
> Далее что происходит в этой строчке: _changedDepths[depth] = wnd; ? Откуда мы знаем, что в словаре _changedDepths есть элемент с ключом depth?
Ну, так они для того и называются синхронизированными

Их вначале инициализируют "одинаково" (т.е. синхронизованно) по (структурам данных)  выбранным инструментов (каждой из которых соответствует стакан, вернее структура данных стакана), обновления котрых нас интересуют, а потом их танцуют

Детали можно посмотреть с помощью ReSharper
FlashPlayer
FlashPlayer Autor

FlashPlayer: Насколько я понимаю, котировки дублируются в 2х словарях и при их обновлении их надо скопировать (синхронизировать) из одного в другой

Вот тут тоже спорно и непонятно, ведь именно в TraderOnMarketDepthsChanged никакого обновления ни одной котировки не происходит - тут то и беда. Оно происходит только в диспатчере этом. И вот такая запутанность непонятна. В TraderOnMarketDepthsChanged только копирование по ссылке значений происходит (опять же непонятно зачем). Короче совсем каша какая-то.

FlashPlayer
FlashPlayer Autor

Огромное спасибо за такой развернутый ответ. Все практически встало на свои места. Остался один вопрос по теме - как все таки работает "синхронизация" мд этими двумя словарями? Вот мы их проинициализировали - два словаря с одинаковыми значениями, но разными ключами. Неужели этого достаточно для того, чтобы если в одном словаре появился новый элемент, то он появился и в другом? Я честно гуглил,но так и не понял как работает этот класс.

И есть еще один вопрос, который связан с моими непониманием некоторых особенностей языка. Помогите пожалуйста также наглядно разобраться с объектом класса GuiDispatcher : _dispatcher. Вот в том же примере в том же классе SecuritiesWindow встречается такой код:


_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 и результат по идее дб тот же. Или нет? Спасибо заранее за пояснения. А то оч уж сложная механика, для вроде кажущихся несложный действий.

Moadip
Moadip

Как я понимаю, что тут происходит: AddPeriodicalAction, как следует из названия, вызывает с какой-то (с какой???) периодичностью нижеидущее действие, которое, в свою очередь, заполняет QuotesWindow из MarketDepth. Как же все таки истинно работает этот диспатчер - в чем его обязанность, удобство перед таймером или вообще необходимость? Библиотека Ecng.Xaml не обфусцирована. И когда возникают вопросы как же оно там все внутри устроено, можно открыть и посмотреть код Reflector или R#. Как бесплатная альтернатива dotPeek.

Открываем, смотрим:


    public void AddPeriodicalAction(Action action)
    {
      if (action == null)
        throw new ArgumentNullException("action");
      this._periodicalActions.Add(action);
      this.StartTimer();
    }

Переходим по StartTimer:


    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. Дальше интересна строчка:


this._timer.Interval = new TimeSpan(this.Interval.Ticks / 10L);

Точнее this.Interval.Ticks. Переходим по Interval и видим следующее:


    public TimeSpan Interval
    {
      get
      {
        return this._interval;
      }
      set
      {
        if (value <= TimeSpan.Zero)
          throw new ArgumentOutOfRangeException("value");
        this._interval = value;
        this.StopTimer();
        this.StartTimer();
      }
    }

И последний переход по _interval, в то место где данное поле инициализируется:


private TimeSpan _interval = TimeSpan.FromMilliseconds(100.0);

В итоге по умолчания AddPeriodicalAction работает с периодичностью = TimeSpan.FromMilliseconds(100.0).Ticks / 10L.

FlashPlayer
FlashPlayer Autor

Хм, спасибо снова за такие пояснения. Не все понятно в середине, но важен итог - получается AddPeriodicalAction вызывается каждые 10мс? Тогда остается открытой эта часть моего вопроса:

" Ведь теперь получается совершенно запутанная последовательность действия, как я понимаю. Вот у нас есть два словаря, пока что пустых. Пользователь кликает по кнопке создать стакан и в словарь _quotesWindows добавляется элемент А = (Security, QuotesWindow). По идее, следуя из того, что словари синхронизированы (хотя я до сих пор не понимаю, как это осуществляется, где и кем) в словаре _changedDepths появляется элемент В = (MarketDepth, QuotesWindow). Причем А и В имеют одно и тоже значение,а ключи разные, хотя и соответствуют одному и тому же инструменту. Далее в TraderOnMarketDepthsChanged по каждому обновленному MarketDepth делается что-то невероятное: ищется соответствующий ему элемент в _quotesWindows и соответствующий ему элемент в _changedDepths, после этого QuotesWindow из первого присваивается второму. Зачем?!Вот этот момент мне не понятен. Далее, как раз с помощью диспатчера (что мне тоже непонятно, но выше я уже это спросил) в _changedDepths во всех элементах берутся котировки из ключа и пихаются в значение. Таким образом мы наконец-то получаем обновленный QuotesWindow (то есть в стакане нашем, наконец-то появляются обновленные котировки). Но это же адово сложный процесс.

Ведь можно, как вы сказали выше, вести только один словарь _changedDepths, и в событии TraderOnMarketDepthsChanged делать то, что делается в _dispatcher.AddPeriodicalAction и результат по идее дб тот же. Или нет? Спасибо заранее за пояснения. А то оч уж сложная механика, для вроде кажущихся несложный действий. "

И к тому же добавляется еще один - а не накладно каждые 10мс вызывать обновление всего списка котировок, вместо того, чтобы обновлять его по событию?

Геннадий Ванин (Gennady Vanin)

FlashPlayer: Ведь теперь получается совершенно запутанная последовательность действия, как я понимаю. Вот у нас есть два словаря, пока что пустых. Пользователь кликает по кнопке создать стакан и в словарь _quotesWindows добавляется элемент А = (Security, QuotesWindow). По идее, следуя из того, что словари синхронизированы (хотя я до сих пор не понимаю, как это осуществляется, где и кем) в словаре _changedDepths появляется элемент В = (MarketDepth, QuotesWindow). Причем А и В имеют одно и тоже значение,а ключи разные, хотя и соответствуют одному и тому же инструменту.

Ну, дак в соответствии с Вами же процитированным кодом:

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 и результат по идее дб тот же Делайте Если бы мне нужно было бы, я бы сделал

FlashPlayer
FlashPlayer Autor

Понятно, то есть одним словом в примере немного мудрено реализован механизм?