2013-11-13 2 views
1

У меня проблема с Control.BeginInvoke(), которая не происходит каждый раз. Кажется, что он работает хорошо 5 раз или около того, а затем становится более успешным после 50/50.BeginInvoke() Call Delayed

Что происходит, так это то, что я назову BeginInvoke() на элемент управления, который уже был создан и отображается, и вызов не будет выполняться в потоке графического интерфейса. Вызывающая нить делает немедленно возвращается с BeginInvoke() и продолжает свою деятельность. Делегат не выполняется в потоке GUI до тех пор, пока на элементе управления не будет выполнено другое BeginInvoke(). Тогда это похоже на то, что он понимает, что это что-то делать и, наконец, делает это. Это может быть более 10 минут спустя.

Код

Вот код, который выполняет BeginInvoke(), которое не выполняет делегат сразу:

protected void DisplayPrompt(PromptData promptData) 
{ 
    Debug.WriteLine(DateTime.Now + ": Entering DisplayPrompt: " + Thread.CurrentThread.Name); 
    this.CurrentPrompt = promptData; 

    if (this.InvokeRequired) 
    { 
     Debug.WriteLine(DateTime.Now + ": Before BeginInvoke of DisplayPrompt: " + Thread.CurrentThread.Name); 
     this.BeginInvoke(new Action(() => this.DisplayPrompt(promptData))); 
     Debug.WriteLine(DateTime.Now + ": After BeginInvoke of DisplayPrompt: " + Thread.CurrentThread.Name); 
     return; 
    } 

    // The rest of the method goes here 
} 

Что кажется пинать вызывается метод через сообщение насос, когда код ниже выполняется. Этот код запускается после того, как я устал ждать выполнения вышеуказанного делегата, и я нажимаю кнопку, которая в конечном счете вызывает ClearPrompt().

protected void ClearPrompt() 
{ 
    Debug.WriteLine(DateTime.Now + ": Entering ClearPrompt: " + Thread.CurrentThread.Name); 
    this.CurrentPrompt = null; 

    if (this.InvokeRequired) 
    { 
     Debug.WriteLine(DateTime.Now + ": Before BeginInvoke of ClearPrompt: " + Thread.CurrentThread.Name); 
     this.BeginInvoke(new Action(() => this.ClearPrompt())); 
     Debug.WriteLine(DateTime.Now + ": After BeginInvoke of ClearPrompt: " + Thread.CurrentThread.Name); 
     return; 
    } 

    // The rest of the method goes here 
} 

И, наконец, вот выход:

11/13/2013 1:16:49 PM: Entering DisplayPrompt: BatchStateMachine 
11/13/2013 1:16:49 PM: Before BeginInvoke of DisplayPrompt: BatchStateMachine 
11/13/2013 1:16:49 PM: After BeginInvoke of DisplayPrompt: BatchStateMachine 
The thread 0x998 has exited with code 0 (0x0). 
The thread 0x1174 has exited with code 0 (0x0). 
11/13/2013 1:27:01 PM: Entering ClearPrompt: BatchStateMachine 
11/13/2013 1:27:01 PM: Before BeginInvoke of ClearPrompt: BatchStateMachine 
11/13/2013 1:27:01 PM: After BeginInvoke of ClearPrompt: BatchStateMachine 
11/13/2013 1:27:01 PM: Entering DisplayPrompt: 
11/13/2013 1:27:01 PM: Entering ClearPrompt: 

Это второй Entering DisplayPrompt в 1:27:01 PM именно там GUI, наконец, работает делегат.

Вопрос: Так что я думаю, вопрос в том, что может привести к BeginInvoke(), чтобы это замедленное поведение, и что я могу сделать, чтобы лучше отладки этого?

Другие ноты:

  1. Эти методы относятся к экземпляру UserControl объекта
  2. Объект UserControl существовавшего и отображается в течение нескольких минут до того, как код побежал, то есть ручки управления должен существовать.
  3. Я использовал BeginInvoke() успешно так же, как это много раз до
  4. GUI, похоже, полностью реагирует на время, в течение которого делегат должен быть запущен. Элементы управления графическим интерфейсом обновляются, я могу перемещаться по приложению и т. Д.

Спасибо!

+0

Когда вы создаете что угодно, возможно, что к нему обратился фоновый поток до того, как он был создан на основном потоке случайно (или был создан в потоке, отличном от основного потока)? –

+0

Вы имеете в виду, что он выполняется, но иногда откладывается или пропущен? –

+0

@ScottChamberlain: я добавил точку останова для каждого открытого/защищенного элемента класса, и я вижу, что он создается и доступен только по основному потоку, прежде чем он будет отображаться. – user955629

ответ

0

Благодаря комментарию Адама Мараса, я считаю, что я отследил основную причину проблемы. Действительно, было выполнено большое количество звонков BeginInvoke(), но это было не сразу очевидно. У меня есть другой элемент управления, который получает значительное количество обновлений из фонового потока. Недостаточно, чтобы вызвать проблему нормально, но проблема заключалась в увеличении числа экземпляров этого элемента управления, которые все получали обновления и выполняли BeginInvoke() для обновления графического интерфейса.

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

Таким образом, хотя графический интерфейс все еще реагировал в некоторой степени, он не смог выполнить все входящие асинхронные вызовы. Я прочитал here, что сообщения SendMessage фактически не добавляются в очередь сообщений, например, сообщения PostMessage, но выполняются непосредственно сообщением насоса и имеют более высокий приоритет, чем очередь сообщений, заполненная PostMessage. Таким образом, если обычные нажатия кнопок и System.Windows.Forms.Timer события обрабатываются через SendMessage событий, а BeginInvoke() вызовы обрабатываются в очереди сообщений, я мог видеть, как это может произойти.

Спасибо за все полезные и быстрые предложения! Кто знает, сколько времени я, возможно, потратил на это!