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

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

18 голосов
/ 13 ноября 2008

Время от времени графический интерфейс моих приложений перестает перерисовываться. Существует множество потоков, которые запускают все виды событий (например, таймеры или сетевые данные готовы и т. Д.). Также есть много элементов управления, которые подписываются на эти события. Из-за этого все обработчики событий играют в игру InvokeRequired / Invoke. Теперь я выяснил, что когда графический интерфейс зависает, многие потоки ожидают возврата Invoke (). Похоже, сообщение насос перестал качать. Обработчики выглядят так:

private void MyEventHandler( object sender, EventArgs e ) {
    if ( InvokeRequired ) {
        Invoke( new EventHandler( MyEventHandler ), sender, e );
        return;
    }

    SetSomeStateVariable();
    Invalidate();
}

Есть идеи?

Решение: BeginInvoke (). Похоже, вы всегда должны использовать BeginInvoke (), если у вас много CrossThread-событий ...

Спасибо.

Спасибо всем.

РЕДАКТИРОВАТЬ: Похоже, BeginInvoke() действительно решил это. Замораживания до сих пор нет.

Ответы [ 4 ]

27 голосов
/ 13 ноября 2008

Invoke ожидает, пока событие не будет обработано в потоке GUI. Если вы хотите, чтобы он был асинхронным, используйте BeginInvoke ()

6 голосов
/ 13 ноября 2008

Возможно, тупик? Вы уверены, что события никогда не запускаются, удерживая блокировку?

Вы можете видеть это с прикрепленным отладчиком? Если это так, остановите его, а затем нажмите кнопку «пауза» - и посмотрите, что делает поток пользовательского интерфейса.

Обратите внимание, что если вам удастся сойти с BeginInvoke вместо Invoke, жизнь станет немного проще, поскольку она не заблокируется.

Также обратите внимание, что вам не нужен бит "new EventHandler" - просто

Invoke((EventHandler) MyEventHandler, sender, e);

должно быть в порядке.

3 голосов
/ 13 ноября 2008

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

Я предлагаю вам создать статический класс, единственная цель которого - обрабатывать все ваши вызовы Invoke. Я бы предположил, что у этого класса есть метод, который принимает Control, (для вызова Invoke) Action (метод, который должен быть вызван) и описание (содержащее информацию, которую вам нужно знать, чтобы идентифицировать метод и его значение). собираюсь сделать).

В теле этого метода я предлагаю вам поставить эту информацию в очередь (метод, описание) и немедленно вернуться.

Очередь должна обслуживаться одним потоком, который извлекает пару действие / сообщение из очереди, записывает текущее время и описание действия в паре свойств, а затем вызывает () действие. Когда действие возвращается, описание и время очищаются (ваш DateTime может быть обнуляемым или установить его в DateTime.Max). Обратите внимание, поскольку все вызовы маршалируются по одному в поток пользовательского интерфейса, вы ничего не теряете, обслуживая очередь одним потоком здесь.

Теперь, вот где мы подходим к сути. У нашего класса Invoking должен быть сердечный поток System.Threading.Timer. Это НЕ должен быть объект windows.forms.timer, так как он выполняется в потоке пользовательского интерфейса (и будет заблокирован, когда пользовательский интерфейс заблокирован !!!).

Работа этого таймера заключается в том, чтобы периодически заглядывать во время текущего действия. Если DateTime.Now - BeginTime> X, таймер пульса решит, что это действие заблокировано. Таймер сердцебиения будет записывать (однако вы регистрируете) ОПИСАНИЕ, записанное для этого действия. Теперь у вас есть запись того, что происходило в то время, когда ваш пользовательский интерфейс заблокирован, и вы можете отладить его лучше.

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

0 голосов
/ 13 ноября 2008

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

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

...