Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Invoke() блокирует

Автор вопроса: EricSchaeferОтветов: 4

Время от времени 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() действительно решил эту проблему. Пока не заморочился.

  • Кого выстраивают, когда он замерзает? Это один конкретный элемент управления или событие, или оно случайное? 13.11.2008
  • Я не очень понимаю, что вы имеете в виду. EricSchaefer 13.11.2008
  • Ваши события передаются в поток пользовательского интерфейса по одному. Кто-то блокирует во время этого процесса. Я спрашиваю, упорядочивается ли один и тот же метод каждый раз, когда блокируется, и/или обновляется ли один и тот же элемент управления, который блокируется каждый раз. 13.11.2008
  • Я не знаю, потому что я ловил это в отладчике только один раз. Время от времени означает, может быть, десять раз за 6 месяцев и в этом случае только один раз на коробке с отладчиком. EricSchaefer 13.11.2008

winforms

Ответы

Ответ 1 · рейтинг 32 · Lou Franco

Invoke ожидает, пока событие не будет обработано в потоке GUI. Если вы хотите, чтобы это было асинхронно, используйте 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, таймер пульса решит, что это действие заблокировано. Таймер сердцебиения будет ЗАПИСЫВАТЬ (как бы вы ни регистрировали) ОПИСАНИЕ, записанное для этого Действия. Теперь у вас есть запись того, что происходило в то время, когда ваш пользовательский интерфейс заблокирован, и вы можете лучше его отлаживать.

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

Ответ 4 · Marc Gravell

Наиболее вероятный ответ (тупик) уже был предложен.

Другой способ имитировать такое поведение - уменьшить количество потоков пула и портов завершения ввода-вывода; вы случайно не звонили ThreadPool.SetMaxThreads(...)?

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы