Ожидание занятости в C#

#c# #multithreading #concurrency

#c# #многопоточность #параллелизм

Вопрос:

Как реализовать ожидание занятости не совсем неэффективным способом? Я сталкиваюсь с проблемой, заключающейся в том, что я могу загружать данные моей модели только методом извлечения, что означает, что я должен вызывать методы getXYZ () непрерывным способом.

Это должно происходить недостаточно быстро для взаимодействия с пользователем, но достаточно быстро, чтобы при изменении состояния в графическом интерфейсе модель могла быть замечена и новое состояние было получено методами getXYZ().

Мой подход просто:

 while (c.hasChanged()) {
   Thread.sleep(500);
}
updateData();
  

Существуют ли механизмы получше?

Комментарии:

1. посмотрите на фоновые потоки (BackgroundWorker или naked threads)

2. Пожалуйста, укажите, какое приложение опрашивает модель здесь.

Ответ №1:

Ваша проблема, похоже, решаема с помощью многопоточности.

В WPF вы можете сделать:

 Thread t = new Thread((ThreadStart)delegate() {
   while (true) {
      Thread.sleep(500);
      if (c.hasChanged())
          Dispatcher.Invoke((Action)delegate() {updateData();});
   }

}).Start();
  

В WinForms

 Thread t = new Thread((ThreadStart)delegate() {
   while (true) {
      Thread.sleep(500);
      // this must derive from Control
      if (c.hasChanged())
          this.Invoke((Action)delegate() {updateData();});
   }

}).Start();
  

Могут отсутствовать параметры для вызова (которые необходимы для выполнения кода в вызывающем потоке пользовательского интерфейса), но я пишу это из своего мозга, поэтому в распоряжении intellisense нет: D

В .NET 4 вы можете использовать TaskFactory.StartNew вместо того, чтобы создавать поток самостоятельно. В .Net <= 4 вы могли бы использовать TreadPool для потока. Однако, я помню, вам нужно, чтобы это было запущено немедленно, потому что вы ожидаете, что оно будет проверено как можно скорее, а пул потоков не гарантирует вам этого (он может быть уже заполнен, но не очень вероятно :-). Просто не делайте глупостей, таких как создание большего их количества в цикле!

И внутри потока вы должны поставить проверку типа

 while (!Closing)
  

чтобы поток мог завершиться, когда вам это нужно, без необходимости прибегать к таким плохим вещам, как t.Abort();
An при выходе установите для закрытия значение true и выполните t.Join() , чтобы закрыть поток проверки.

Редактировать:

Я забыл сказать, что закрытие должно быть свойством bool или ИЗМЕНЧИВЫМ логическим значением, а не простым логическим значением, потому что вам не будет гарантировано, что поток может когда-либо завершиться (ну, это было бы в случае, если вы закрываете приложение, но хорошей практикой является заставить их завершаться по вашей воле). ключевое слово volatile предназначено для предотвращения применения (псевдо) компилятором каких-либо оптимизаций к коду, которые предполагают, что значения переменных не могут измениться

Комментарии:

1. насколько я понимаю, VOLATILE никогда не следует использовать, за исключением самых тривиальных программ. Это небезопасно не только для процессоров AMD и Itanium. Джозеф Альбахари также получил простой тестовый код для процессоров Intel, который демонстрирует, что volatile и здесь небезопасен — albahari.com/threading/part4.aspx#_The_volatile_keyword

2. @John Я согласен с вами, но поскольку это конкретное изменчивое логическое значение будет прочитано в цикле, на самом деле не так важно, будет ли оно прочитано неправильно один или два раза (не имеет значения, связано ли это с когерентностью кэша или чем-то еще, в какой-то момент оно будет перечитано). Что будет неправильно, так это если компилятор заменит while (bClosing) на while (true). Я не эксперт по фреймворкам, но в выдержке из документации говорится, что компилятор никогда не будет выполнять оптимизацию для изменчивых переменных. В любом случае, лучший подход — сделать это свойством.

Ответ №2:

Из вашего сообщения неясно, что именно вы пытаетесь сделать, но похоже, что вам следует поместить ваши вызовы модели / службы в отдельный поток (через фоновый рабочий процесс или асинхронный делегат) и использовать обратный вызов из вызова модели / службы, чтобы уведомить пользовательский интерфейс, когда это будет сделано. Затем ваш поток пользовательского интерфейса может выполнять загруженные задачи, например показывать индикатор выполнения, но не перестать отвечать.

Ответ №3:

Если вы проводите опрос с помощью графического интерфейса, используйте (WinForms) Таймер.

Если это какой-то фоновый процесс, ваш Sleep () может быть меньшим злом.

Ответ №4:

Явное ожидание занятости является злом, и его следует избегать, когда это возможно.

Если вы не можете избежать этого, тогда создайте свое приложение, используя шаблон проектирования Observer, и зарегистрируйте заинтересованные объекты в объекте, который выполняет опрос, поддерживаемый потоком.

Таким образом, у вас есть чистый дизайн, ограничивающий уродливый материал только в одном месте.