Зависание при вызове Control.Invoke (с решением)
От: WW898 Германия  
Дата: 03.04.07 08:15
Оценка: 18 (4) +1 :)
#Имя: FAQ.dotnet.invoke.hangup
Симптомы:
Приложение зависает при вызове Control.Invoke() для фреймворка 2.0, под 1.1 все работает изумительно.

Вскрытие показало:
Не приходит эвент об окончании выполнения функции которую хотели вызвать через Control.Invoke(), более того сама функция, которую нужно вызвать в другой нитке, не вызывается вобще, однако запрос поставлен в очередь контрола. Дальнейшее исследование вопроса выявило, что выполнение очереди происходит по получении контролом специального зарегистренного сообщения "WindowsForms12_ThreadCallbackMessage". Вот тут то и выяснилось, что это сообщение вобще не посылается. Ниже приведен дизасмблеж кода с ошибкой.

private object MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous)
{
    int lpdwProcessId;
    if (!this.IsHandleCreated)
        throw new InvalidOperationException(SR.GetString("ErrorNoMarshalingThread"));
    if (((ActiveXImpl) this.Properties.GetObject(PropActiveXImpl)) != null)
        IntSecurity.UnmanagedCode.Demand();
    bool flag = false;
    if ((SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, this.Handle), out lpdwProcessId) == SafeNativeMethods.GetCurrentThreadId()) && synchronous)
        flag = true;
    ExecutionContext executionContext = null;
    if (!flag)
        executionContext = ExecutionContext.Capture();
    ThreadMethodEntry entry = new ThreadMethodEntry(caller, method, args, synchronous, executionContext);
    lock (this)
    {
        if (this.threadCallbackList == null)
            this.threadCallbackList = new Queue();
    }
    lock (this.threadCallbackList)
    {
        if (threadCallbackMessage == 0x0)
            threadCallbackMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_ThreadCallbackMessage");
        this.threadCallbackList.Enqueue(entry); // Ставим запрос в очередь вызываемого контрола
    }
    if (flag)
        this.InvokeMarshaledCallbacks(); // Если можно вызвать функцию синфронно, то вызываем синхронно
    else
    {
        UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero); // Вызываем функцию асинхронно
        // БАГА ТУТ: нет проверки на невозможность поставить сообщение в очередь
    }
    if (!synchronous)
        return entry;
    if (!entry.IsCompleted)
        this.WaitForWaitHandle(entry.AsyncWaitHandle); // Ждем окончания выполнения для асинхронного вызова
    if (entry.exception != null)
        throw entry.exception;
    return entry.retVal;
}


Причина смерти:
Горячо любимый Microsoft забыл выдать эксепшен в случае невозможности отсылки сообщения с помощью PostMessage. Оказалось, что на момент Control.Invoke() очередь сообщений заполнена на 100%!!!

Лечение:
— по таймеру посылать "WindowsForms12_ThreadCallbackMessage" контолу (не кошерно, но когда нет другого выхода, то сойдет);
— сделать Thread.Sleep() на пару секунд для очистки очереди в вызывающем треде (не наш метод, мы боремся за скорость);
— устранить причину заполнености очереди сообщений.
Re: Зависание при вызове Control.Invoke (с решением)
От: tyger Россия  
Дата: 04.04.07 04:12
Оценка:
Здравствуйте, WW898, Вы писали:

WW>Симптомы:

WW>Приложение зависает при вызове Control.Invoke() для фреймворка 2.0, под 1.1 все работает изумительно.

WW>Вскрытие показало:

WW>Не приходит эвент об окончании выполнения функции которую хотели вызвать через Control.Invoke(), более того сама функция, которую нужно вызвать в другой нитке, не вызывается вобще, однако запрос поставлен в очередь контрола. Дальнейшее исследование вопроса выявило, что выполнение очереди происходит по получении контролом специального зарегистренного сообщения "WindowsForms12_ThreadCallbackMessage". Вот тут то и выяснилось, что это сообщение вобще не посылается. Ниже приведен дизасмблеж кода с ошибкой.

Вопрос не по теме: Каким образом отлаживали системные функции??? Или просто изучали код с помощью какого-нибудь рефлектора и анализировали его?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: Зависание при вызове Control.Invoke (с решением)
От: WW898 Германия  
Дата: 04.04.07 08:32
Оценка:
Здравствуйте, tyger, Вы писали:

T>Вопрос не по теме: Каким образом отлаживали системные функции??? Или просто изучали код с помощью какого-нибудь рефлектора и анализировали его?

Отлаживал с ассемблере кусочек и анализ с помощью рефлектора.

Дополнительно скажу в чем была загвоздка конкретно в моем приложении:
Есть 2 нитки: главная и рабочая. Рабочая передает в главную с помошью BeginInvoke информацию о прогрессе операции для окна №1. В конце рабочая вызывает Invoke для окна №2 и зависает. Рабочая нитка не успевала разгребать поставленые в очередь сообщения для окна №1, поэтому сообщение для окна №2 было ТИХО откинуто. Все зависло. После кардинального уменьшения количества BeginInvoke для окна №1 все заработало.
Re: Зависание при вызове Control.Invoke (с решением)
От: vdimas Россия  
Дата: 04.04.07 21:03
Оценка:
Здравствуйте, WW898, Вы писали:

Попробуй вместо Invoke использовать Dispatcher, он специально для подобных нужд создавался.
Re[2]: Зависание при вызове Control.Invoke (с решением)
От: WW898 Германия  
Дата: 05.04.07 10:00
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Попробуй вместо Invoke использовать Dispatcher, он специально для подобных нужд создавался.

Сильно извиняюсь, но я не очень понял что именно вы предлагаете. Не могли бы разъяснить, желательно с примерами. Заранее благодарен.
Re[3]: Зависание при вызове Control.Invoke (с решением)
От: muse Россия  
Дата: 05.04.07 13:36
Оценка:
Здравствуйте, WW898, Вы писали:

WW>Сильно извиняюсь, но я не очень понял что именно вы предлагаете. Не могли бы разъяснить, желательно с примерами. Заранее благодарен.


Видимо имелся в виду паттерн Dispatcher, иначе Mediator. В статье Обобщенный Model-View-Controller
Автор(ы): Иван Бодягин
Дата: 25.07.2006
В наше время сложно найти разработчика, который не слышал бы о паттерне под названием Model-View-Controller или сокращенно MVC, что вообщем не удивительно, с задачей отделения данных от их представления сталкиваешься практически на каждом проекте. Однако, как ни странно, столь же сложно найти разработчика, который действительно четко себе представляет, что такое на самом деле паттерн MVC и как его можно реализовать в конкретной ситуации. Основная причина такой неоднозначности в том, что по историческим причинам данной аббревиатурой принято называть не один единственный паттерн, а целое семейство паттернов, призванное отделять представление от модели. Произошло это в силу разных обстоятельств. Отчасти из-за того что MVC не просто паттерн, а довольно объемное архитектурное решение, в котором каждый новый разработчик видел что-то свое и ставя во главу угла особенности своего проекта, реализовывал его по своему. Отчасти же из-за возраста данного паттерна, во времена его изобретения и сами приложения, и графические интерфейсы были существенно беднее чем в наше время, с тех пор они сильно эволюционировали и вместе с ними изменялся и сам паттерн. Данная статья посвящена также одному из паттернов входящих в это семейство, причинам его появления, особенностям применения, преимуществам и недостаткам, а так же описанию сопутствующих паттернов.
описывается суть его работы в контексте MVC. А так же тут Практика применения паттернов проектирования
Автор(ы): Беркович Вадим, Чудин Андрей
Дата: 09.04.2003
Практически во всех проектах можно встретить те или иные паттерны проектирования. Но далеко не часто они обозначены разработчиками. Проект, в котором явно обозначены все использованные паттерны, удобнее для понимания и более управляем. Можно сказать, что описание проекта в терминах паттернов добавляет новые метаданные о проекте. Если мы считаем, что данный класс реализует паттерн "итератор", мы сразу получаем представление об его интерфейсе и роли. Если же изначально весь проект реализован с использованием паттернов, то управление проектом упрощается. Обобщение удачных решений конкретных задач в паттерны и использование их в последующих проектах существенно ускоряет процесс разработки. А код становится более понятным и элегантным, и им можно будет воспользоваться повторно.
.
Люблю повеселиться, особенно пожрать
Re[4]: Зависание при вызове Control.Invoke (с решением)
От: WW898 Германия  
Дата: 09.04.07 15:25
Оценка:
Здравствуйте, muse, Вы писали:

M>Видимо имелся в виду паттерн Dispatcher, иначе Mediator. В статье Обобщенный Model-View-Controller
Автор(ы): Иван Бодягин
Дата: 25.07.2006
В наше время сложно найти разработчика, который не слышал бы о паттерне под названием Model-View-Controller или сокращенно MVC, что вообщем не удивительно, с задачей отделения данных от их представления сталкиваешься практически на каждом проекте. Однако, как ни странно, столь же сложно найти разработчика, который действительно четко себе представляет, что такое на самом деле паттерн MVC и как его можно реализовать в конкретной ситуации. Основная причина такой неоднозначности в том, что по историческим причинам данной аббревиатурой принято называть не один единственный паттерн, а целое семейство паттернов, призванное отделять представление от модели. Произошло это в силу разных обстоятельств. Отчасти из-за того что MVC не просто паттерн, а довольно объемное архитектурное решение, в котором каждый новый разработчик видел что-то свое и ставя во главу угла особенности своего проекта, реализовывал его по своему. Отчасти же из-за возраста данного паттерна, во времена его изобретения и сами приложения, и графические интерфейсы были существенно беднее чем в наше время, с тех пор они сильно эволюционировали и вместе с ними изменялся и сам паттерн. Данная статья посвящена также одному из паттернов входящих в это семейство, причинам его появления, особенностям применения, преимуществам и недостаткам, а так же описанию сопутствующих паттернов.
описывается суть его работы в контексте MVC. А так же тут Практика применения паттернов проектирования
Автор(ы): Беркович Вадим, Чудин Андрей
Дата: 09.04.2003
Практически во всех проектах можно встретить те или иные паттерны проектирования. Но далеко не часто они обозначены разработчиками. Проект, в котором явно обозначены все использованные паттерны, удобнее для понимания и более управляем. Можно сказать, что описание проекта в терминах паттернов добавляет новые метаданные о проекте. Если мы считаем, что данный класс реализует паттерн "итератор", мы сразу получаем представление об его интерфейсе и роли. Если же изначально весь проект реализован с использованием паттернов, то управление проектом упрощается. Обобщение удачных решений конкретных задач в паттерны и использование их в последующих проектах существенно ускоряет процесс разработки. А код становится более понятным и элегантным, и им можно будет воспользоваться повторно.
.

Однако, попрежнему, мне не совсем понятно какое всё это отношение имеет к проблеме взаимодействия двух потоков, через вызовы Control.Invoke()?

P.S. Так же прошу учесть, что все обязано компилироваться и работать под .NET фреймворк 1.0.
Re[5]: Зависание при вызове Control.Invoke (с решением)
От: vdimas Россия  
Дата: 09.04.07 22:57
Оценка:
Здравствуйте, WW898, Вы писали:


WW>P.S. Так же прошу учесть, что все обязано компилироваться и работать под .NET фреймворк 1.0.


А нафига под 1.0? Нет вообще ни одной причины продолжать его поддержку.

Имелся ввиду объект Dispatcher из .Net 3.0, который недавно вышел.
Re[6]: Зависание при вызове Control.Invoke (с решением)
От: WW898 Германия  
Дата: 10.04.07 10:25
Оценка:
Здравствуйте, vdimas, Вы писали:

V>А нафига под 1.0? Нет вообще ни одной причины продолжать его поддержку.

У вас нет, а у нас есть. "Претензии на устройство мира не принимаются." (С)

V>Имелся ввиду объект Dispatcher из .Net 3.0, который недавно вышел.

А что меняется? Загляните в Dispatcher.RequestForegroundProcessing(). Там вы увидите все тот же PostMessage, НО:
— повторная постановка сообщения в очередь не производиться;
— корректно обрабатываются ситуация, когда невозможно поставить сообщение в очередь.

Ну что тут сказать: умеет Микрософт делать работу над ошибками когда хочет. Вот если бы тоже самое сделали бы и с Control.Invoke(), но видно не судьба...

private bool RequestForegroundProcessing()
{
    // Yt bvttn 
    if (this._postedProcessingType >= 0x2)
    {
        return true;
    }
    if (this._postedProcessingType == 0x1)
    {
        SafeNativeMethods.KillTimer(new HandleRef(this, this._window.Value.Handle), 0x1);
    }
    this._postedProcessingType = 0x2;
    return UnsafeNativeMethods.TryPostMessage(new HandleRef(this, this._window.Value.Handle), _msgProcessQueue, IntPtr.Zero, IntPtr.Zero);
}
Re[7]: Зависание при вызове Control.Invoke (с решением)
От: vdimas Россия  
Дата: 10.04.07 14:06
Оценка:
Здравствуйте, WW898, Вы писали:


WW>Ну что тут сказать: умеет Микрософт делать работу над ошибками когда хочет. Вот если бы тоже самое сделали бы и с Control.Invoke(), но видно не судьба...


Contorl.Invoke() — это приблуда растет из-за заточенности GUI-домена на STA, из-за ActiveX, ну и плюс оконный мессейджинг привязан к очереди сообщений потока, в котором создавалось окно. Т.е. нативное окошко к треду привязано намертво.

Ты лучше скажи, зачем тебе BeginInvoke()? Почему не сразу Invoke()? И как можно было умудриться забить очередь до предела?
Зачем посылать статус окну постоянно? Можно просто запомнить время последней отсылки и посылай только через нек. интервал.
Re[8]: Зависание при вызове Control.Invoke (с решением)
От: WW898 Германия  
Дата: 10.04.07 16:24
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Contorl.Invoke() — это приблуда растет из-за заточенности GUI-домена на STA, из-за ActiveX, ну и плюс оконный мессейджинг привязан к очереди сообщений потока, в котором создавалось окно. Т.е. нативное окошко к треду привязано намертво.

А что поменялось-то в диспатчере? Как было все привязано к очереди сообщений так и осталось. Просто корректнее сделали и вокруг разного удобства понавесили. А так — один в один.

V>Ты лучше скажи, зачем тебе BeginInvoke()? Почему не сразу Invoke()? И как можно было умудриться забить очередь до предела?

Invoke дольше работает за счет ожидания результата, а результат-то выполнения как раз нам и не нужен.
Умудриться? На то талант нужен! И он есть! Жаль правда, что не у меня.

V>Зачем посылать статус окну постоянно? Можно просто запомнить время последней отсылки и посылай только через нек. интервал.

Все уже с неделю как починено по методу "что НЕ делается — все к лучшему" (С) .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.