#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, и зарегистрируйте заинтересованные объекты в объекте, который выполняет опрос, поддерживаемый потоком.
Таким образом, у вас есть чистый дизайн, ограничивающий уродливый материал только в одном месте.