Время от времени GUI моих приложений перестает перерисовываться. Существует множество потоков, которые запускают всевозможные события (например, таймеры или готовность сетевых данных и т. д.). Также есть много элементов управления, которые подписываются на эти события. Из-за этого все обработчики событий играют в игру InvokeRequired/Invoke. Теперь я понял, что когда графический интерфейс зависает, многие потоки ждут возврата Invoke(). Похоже, насос сообщений перестал качаться. Обработчики выглядят так:
private void MyEventHandler( object sender, EventArgs e ) {
if ( InvokeRequired ) {
Invoke( new EventHandler( MyEventHandler ), sender, e );
return;
}
SetSomeStateVariable();
Invalidate();
}
Любые идеи?
Решение: BeginInvoke(). Похоже, вы всегда должны использовать BeginInvoke(), если у вас много событий CrossThread...
Спасибо.
Спасибо всем.
EDIT: Похоже, BeginInvoke() действительно решил эту проблему. Пока не заморочился.
Ответ 2 · рейтинг 6 · Jon Skeet
Может тупик? Вы следите за тем, чтобы события никогда не запускались при удерживании блокировки?
Вы можете увидеть это с подключенным отладчиком? Если это так, заморозьте его, а затем нажмите кнопку "пауза" - и посмотрите, что делает поток пользовательского интерфейса.
Обратите внимание, что если вы можете использовать BeginInvoke вместо Invoke, жизнь становится немного проще, поскольку она не блокируется.
Также обратите внимание, что вам не нужен бит "новый EventHandler" - просто
Invoke((EventHandler) MyEventHandler, sender, e);
все должно быть в порядке.
Ответ 3 · рейтинг 4 · Community
Наблюдая за этим вопросом, я вижу, что вы не получите никаких ответов, которые немедленно решат проблему, поскольку большинство из них требуют от вас отладки события, а это происходит так редко, что это почти невозможно. Итак, позвольте мне предложить вам внести некоторые изменения в код, которые могут помочь вам определить виновника в полевых условиях.
Я предлагаю вам создать статический класс, единственной целью которого является обработка всех ваших вызовов Invoke. Я бы предположил, что у этого класса есть метод, который принимает элемент управления (для вызова Invoke), действие (метод, который нужно вызвать) и описание (содержащее информацию, которую вам нужно знать, чтобы идентифицировать метод и что это такое). собираюсь сделать).
В теле этого метода я предлагаю вам поставить в очередь эту информацию (метод, описание) и немедленно вернуться.
Очередь должна обслуживаться одним потоком, который выталкивает пару действие/сообщение из очереди, записывает текущее время и описание действия в пару свойств, а затем вызывает действие. Когда действие возвращается, описание и время очищаются (ваш DateTime может принимать значение NULL или устанавливать его в DateTime.Max). Обратите внимание: поскольку все вызовы маршалируются по одному в поток пользовательского интерфейса, вы ничего не теряете, обслуживая здесь очередь одним потоком.
Теперь, вот где мы добираемся до сути этого. Наш класс Invoking должен иметь поток пульса System.Threading.Timer. Это НЕ должен быть объект windows.forms.timer, поскольку он выполняется в потоке пользовательского интерфейса (и будет заблокирован, когда пользовательский интерфейс заблокирован!!!).
Работа этого таймера состоит в том, чтобы периодически просматривать время, когда текущее действие было вызвано. Если DateTime.Now - BeginTime > X, таймер пульса решит, что это действие заблокировано. Таймер сердцебиения будет ЗАПИСЫВАТЬ (как бы вы ни регистрировали) ОПИСАНИЕ, записанное для этого Действия. Теперь у вас есть запись того, что происходило в то время, когда ваш пользовательский интерфейс заблокирован, и вы можете лучше его отлаживать.
Я знаю, что это не ответ на вашу проблему, но, по крайней мере, делая это, вы можете получить хорошее представление о том, что происходит в то время, когда вы заблокированы.