none
События и многопоточность RRS feed

  • Вопрос

  • В книжках отдельно разобрана многопоточность и отдельно разобраны события. Я сделал событие по примеру из книжки, и у меня обработчики события запускаются в том же потоке, в котором это событие было опубликовано, а при попытке обращения к данным события из другого потока, я получаю ошибки многопоточности.

    К примеру, по таймеру делается запрос данных с сервера, затем пришедшие данные помещаются в производный от EventArgs объект, публикуется событие и срабатывает обработчик. Ниже код метода обработчика:

        private void NewDataHandler(object sender, NewDataEventArgs e)
        {
           //Обработчик срабатывает, и, к примеру, нужно обновить UI в зависимости от данных
           RefreshUI(e.Data)
        }
    

    Обновение UI нужно делать в потоке UI, поэтому тут делаю так:

        private void RefreshUI(List<Element> elements = null)
        {
          _mainWindow.Dispatcher.BeginInvoke(new ThreadStart(() =>
          {
            if (elements != null && elements.Count > 0)
            {
              foreach (var element in elements)
              {
                   //тут уже какие-то действия по обновлению, в которых elements только читается 
              }
            }
          }));
        }
    

    Но я сразу получаю ошибку в foreach (var element in elements) - коллекция была изменена, перечисление выполнить невозможно. Причина, очевидно, в том, что в методе RefreshUI у меня ссылка на данные потока, поднимающего событие, и он начинает их обновлять (обновление идет очень часто). Но как тут быть? Как вообще используется подписка на события, происходящие в другом потоке, и предоставляющие из него данные? Нужно везде блокировать предоставлемые данные?

    22 февраля 2011 г. 16:22

Ответы

  • вы не можете изменять коллекцию элементов по которой бежите форычем

    это коллекция только для чтения

    что вы конкретно делаете с этой коллекцией?

    может ли быть такое что потоки одновременно работают с одной и той же коллекцией,

    т.е. пока первый бежит по ней форычем, второй ее меняет?

    • Помечено в качестве ответа Qwester33 23 февраля 2011 г. 20:02
    23 февраля 2011 г. 10:21
  • List<...> - это reference type. И e.Data, и elements, и поле класса ссылается на один и тот же объект в куче. Изменение его в одном потоке (в том числе очистка) поломает foreach во всех остальных.
    My blog
    • Помечено в качестве ответа Qwester33 23 февраля 2011 г. 19:58
    23 февраля 2011 г. 19:56
    Модератор

Все ответы

  • вы не можете изменять коллекцию элементов по которой бежите форычем

    это коллекция только для чтения

    что вы конкретно делаете с этой коллекцией?

    может ли быть такое что потоки одновременно работают с одной и той же коллекцией,

    т.е. пока первый бежит по ней форычем, второй ее меняет?

    • Помечено в качестве ответа Qwester33 23 февраля 2011 г. 20:02
    23 февраля 2011 г. 10:21
  • Я просмотрел весь код, и не нашел места, где еще использовалась бы коллекция elements или e.Data, помимо места, где она заполняется рабочим потоком. Вот код поднятия события:

     protected virtual void OnNewDataEvent()
     {
     EventHandler<NewDataEventArgs> handler = NewDataEvent;
     if (handler != null)
     handler(this, new NewDataEventArgs(Data));
     }
    

    А то, что во время  foreach (var element in elements), событие поднимается еще раз, не может быть причной? Ведь elements это ссылка на e.Data. Хотя, вроде-бы, для каждого поднятия создается новый экземпляр NewDataEventArgs(Data). Но, возможно, причина в том, что Data не является локальной переменной метода, вызывающего OnNewDataEvent, а является полем класса, и перед вызовом просто очищается через Clear() и заполняется заново? То есть, каждое новое событие, каждый новый экземпляр NewDataEventArgs, хранит ссылку на одну и ту же коллекцию (так как она поле класса, поднимающего событие)?

    23 февраля 2011 г. 19:24
  • List<...> - это reference type. И e.Data, и elements, и поле класса ссылается на один и тот же объект в куче. Изменение его в одном потоке (в том числе очистка) поломает foreach во всех остальных.
    My blog
    • Помечено в качестве ответа Qwester33 23 февраля 2011 г. 19:58
    23 февраля 2011 г. 19:56
    Модератор
  • Спасибо

    23 февраля 2011 г. 20:02