Подтвердить что ты не робот

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

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

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

    SetSomeStateVariable();
    Invalidate();
}

Любые идеи?

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

Спасибо.

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

EDIT: Похоже, что BeginInvoke() действительно решила его. Не замораживать до сих пор.

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

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

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

должно быть хорошо.

Ответ 3

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

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

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

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

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

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

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

Ответ 4

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

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