Я пишу небольшое приложение .NET (Windows Forms), но мне кажется, что я не могу правильно понять многопоточность.
Приложение имеет очень простую логику: оно отображает основную форму с некоторымипараметры для заполнения, кнопка для запуска обработки и индикатор выполнения;обработка начинается, когда пользователь нажимает на кнопку, индикатор выполнения должен обновляться во время работы программы, некоторые MessageBox
могут появляться во время обработки для отображения предупреждений / информации, а когда обработка заканчивается, появляется другое MessageBox
срезультаты, достижения.Довольно просто ... но я не могу найти способ правильно обрабатывать обновления управления.
Сначала я попробовал простой маршрут: выполнить всю свою обработку в событии Button_Clicked.Это не шло хорошо: это работало, но форма перестала отвечать на запросы во время обработки, она не заботилась ни о каком щелчке пользователя.Я предположил, что это потому, что он был действительно занят обработкой события, таким образом прекратил обработку других ... так что я пошел с многопоточностью.
В событии обработки кнопок я запустил новый Thread
и заставил его выполнятьобработка, а затем вызывается Thread.Join
на нем;MSDN documentatin говорит о Thread.Join
: «Блокирует вызывающий поток до тех пор, пока поток не прекратит работу, продолжая при этом выполнять стандартную откачку COM и SendMessage» (http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx). Но, похоже, это не так: после вызова Thread.Join
форма снова перестала отвечать до тех пор, пока поток не завершился. Почему это так? Я отказался от идеи соединения и сразу же вернулся из функции обработки событий (отключение кнопок на форме, чтобы пользователи не запускали более одной обработки одновременно),но потом я столкнулся с другой проблемой: я начал получать исключения при попытке обновить индикатор выполнения, среда выполнения жаловалась на то, что вызвал элемент управления из потока, который не является его владельцем.
Так что я наконец-то выбрал BackgroundWorker
: Я добавил один к форме, назвал его RunWorkerAsync()
методом и использовал ReportProgress()
для запуска события ProgressChanged
и обработки его в основной форме (чтобы избежать проблемы обновления управления несколькими потоками). Я наконец-то смогобновить индикатор выполнения ... но теперь это полностью асинхронно: прогрессПанель s фактически обновляется через некоторый случайный интервал после того, как рабочий поток вызывает ReportProgress()
(даже одну секунду!), и это дает особенно ужасный визуальный эффект.Два примера:
- Если я вызову
ReportProgress()
из рабочего потока, а затем MessageBox.Show()
, чтобы отобразить какое-то сообщение о текущем шаге обработки, я вижу, как индикатор выполнения опережает после выскакивает MessageBox. - У меня есть событие
RunWorkerCompleted
, которое выскакивает последние MessageBox
с результатами, и я вижу, как оно появляется на экране , пока индикатор выполнения все еще заполняетсяпоследние шаги .
Что мне здесь не хватает?