2017-07-06 25 views
0

以下代碼是一個簡化的概念驗證。 在Windows窗體應用程序中有一個FormClosing事件處理程序來執行一些清理。應用程序啓動一些使用Control.Invoke在窗體上寫入的線程。我知道我可以使用Control.BeginInvoke,但我想更好地瞭解發生了什麼並找到另一個解決方案。Control.Invoke出現意外的塊狀況

List<Thread> activeThreadlist; 
volatile bool goOnPolling = true; 
private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
    { 
     goOnPolling = false; 
     Thread.Sleep(1000); 
     foreach (Thread element in activeThreadlist) 
      if (element.IsAlive) 
       element.Abort(); 

    } 

一個標誌被設置爲false,以停止對線程的循環,他們有時間終止,如果一個人還活着,它終止了與中止。

這裏是由其他線程執行的代碼:

while (goOnPolling) 
{ 
    //some long elaboration here 
    if (!goOnPolling) continue; 
    aControl.Invoke(new Action(() => 
    { 
     //display some result about the elaboration 
    })); 
    if (!goOnPolling) continue; 
    //some long elaboration here  

} 

在的情況下爲約50%時關閉窗體上Control.Invoke線程塊,這樣它們不會在睡眠時間終止和Abort被調用。我認爲在調用Invoke之前檢查標誌goOnPolling在99.999%的情況下足夠了,但我錯了。正如代碼所述,線程做了很長的闡述(至少100ms),所以我預計goolnolling可能會在他們期間改變。爲什麼我錯了?是否有另一種簡單的解決方案,而不需要重複創建額外的線程的BeginInvoke?

更新

閱讀評論後,我意識到標題是錯誤的。我最初寫死鎖但它只是一個意想不到的(對我來說)塊條件。一開始我無法理解爲什麼我的線程在等待終端1000ms後仍然存在,所以我把一個中止找出來。我完全誤解了InvokeBeginInvoke之間的區別,謝謝澄清。

@喬恩乙

我同意突破更適合比繼續但代碼裏面,而(goOnPolling)塊,所以我只能節省一些CPU週期,僅此而已。

任何想法爲什麼經常goOnPolling在Invoke的早期階段發生變化?如果我進行長時間的闡述,它應該在大多數時候發生。

+1

'BeginInvoke'不應該創建額外的線程,它只是在*現有* UI線程上排隊工作。創建一個新的線程完全與'BeginInvoke'設計的目的相反。 –

+1

你確定你最後一句話嗎?據我所知,在UI線程上執行'Invoke'和'BeginInvoke',在返回之前只有'Invoke'塊。即'Invoke' ='BeginInvoke' +等待執行。你可能會混淆'委託''Invoke' /'BeginInvoke',請參閱https://stackoverflow.com/questions/229554/whats-the-difference-between-invoke-and-begininvoke。使用'Control.Invoke'的唯一原因是在繼續調用線程之前需要調用的動作完成。 – Rotem

+1

我100%肯定。 'Invoke'在消息循環中排隊執行,然後等待它完成。 'BeginInvoke'完成了完全相同的事情,但在完成之前不會阻塞。這可能是你的僵局來自哪裏。您的UI線程正在等待子線程,並且子線程正在UI上等待完成。 –

回答

0

我們知道這些其他線程想要通過Invoke進入UI線程,並且我們知道您正在將中的UI線程捆綁在一起。

這可能是爲數不多的時期之一,其中Application.DoEvents是最不壞的選擇在這裏:

private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
{ 
    goOnPolling = false; 
    for(int i=0;i<10;i++) 
    { 
     Application.DoEvents(); 
     Thread.Sleep(50); 
    } 
    //foreach (Thread element in activeThreadlist) 
    // if (element.IsAlive) 
    //  element.Abort(); 
} 

,改變它的線程,以應付可能拋出的異常,如果他們試圖Invoke形式早已之後被關閉了,而不是試圖用Abort來撕毀他們。


至於爲什麼這麼多線程都在他們想Invoke在狀態,它是從迄今爲止發佈的代碼不清楚 - 但請記住,Invoke被序列化訪問UI線程。它是可能所有線程在UI線程到達之前詢問Invoke處理如果對線程的訪問嚴重擁塞,則轉換爲「窗體關閉」事件的Windows消息。