2009-07-20 165 views
1

我們有一些代碼在後臺線程它需要彈出一個對話框或其他一些用戶交互運行,所以我們做平常Invoke呼叫到UI線程:調用與超時

Control.Invoke(SomeFunction); 

void SomeFunction() 
{ 
    ... 
} 

但是,我們遇到了一個bug,我們的UI線程有時不會立即響應Invoke調用 - 我們追蹤到UI線程當前正在執行還未返回的跨進程DCOM調用。一旦DCOM調用返回,我們的函數將被調用,但在此之前,似乎Invoke調用已掛起。

我的這個解決方案是引入超時:

ManualResetEvent invokeEvent = new ManualResetEvent(); 
var result = Control.BeginInvoke(SomeFunction, invokeEvent); 

if (!invokeEvent.WaitOne(1000)) 
    throw new Exception("Not responding"); 

Control.EndInvoke(result); 

void SomeFunction(ManualResetEvent invokeEvent) 
{ 
    invokeEvent.Set(); 

    ... 
} 

這曾在「關於我的機器感的作品」,但它有一些缺陷。

http://www.codinghorror.com/blog/images/works-on-my-machine-stamped.png

  • 首先功能仍然調用,即使發生超時 - 如果DCOM調用實際上並沒有完全掛起,它最終會
  • 其次,有明顯的可怕的種族條件
  • 最後是整個事情整個「Arrgh」 -ness

即使第一次兩件事情可以得到解決,我們仍然有根口腔疾病。有沒有更好的方法來解決這個問題?

回答

0

將跨進程DCOM調用移動到另一個線程。你是顯然掛在UI線程,這是完全不能接受的。解決這個問題,你的幻影問題(OP)也會消失。

+0

非常抱歉,我們不擁有使跨進程DCOM調用的代碼。即使我們這樣做了,我們仍然需要在我們自己的代碼中將其作爲一個普遍問題來解決,因爲可能會加載任何其他代碼,這可能會導致這個普遍問題。好主意,但 – 2009-07-20 08:31:30

0

這是一個常見的線程問題,當涉及在GUI線程上運行某些東西時,這種症狀會影響到所有開發人員。

如果您要創建一個單獨的線程來顯示實際的進度對話框和另一個線程來執行DCOM調用,它只需要移動兩個線程之間的ManuaResetEvent同步。這具有不鎖定GUI線程的好處,因爲創建進度表單的單獨線程將創建自己的消息隊列,並且用於運行DCOM調用的第二個線程不必鎖定任何GUI線程。

它確實需要一些小心sycnhronizing但一旦完成,它的美麗在行動上看到:

private ManualResetEvent _event = new ManualResetEvent(false); 
... 

private void StartTheComProgressCall() 
{ 
    _event.Reset(); 

    ThreadPool.QueueUserWorkItem(StartProgressDialog); 
    ThreadPool.QueueUserWorkItem(StartDCOMCall); 

    // there's various possibilities to perform here, we could ideally 1) wait on the 
    // event to complete, 2) run a callback delegate once everything is done 
    // 3) fire an event once completed 
} 

private void StartProgressDialog(object state) 
{ 
    ProgressDialog dialog = new ProgressDialog(); 
    dialog.Show(); 

    while(!_event.WaitOne(0)) 
     Application.DoEvents(); 

    dialog.Close(); 
} 

private void StartDCOMCall() 
{ 
    ... 
    <perform your DCOM routines here> 

    // once the call is done, remember to trigger that it's complete 
    // so that blocking threads can continue to do what they need to do 
    _event.Set(); 
} 

注意 有些人可能會反對使用Application.DoEvents()方法,但考慮到DoEvents強迫任何掛起的Windows消息在當前調用線程的消息隊列中進行處理,並且由於調用是在不同的線程(創建進度對話框的線程)而不是GUI線程中進行的,因此使用它時應該沒有更多或道德上的「代碼異味」問題。我們應該使用任何工具或技術來幫助我們完成工作。