2011-04-02 79 views
2

我正面臨着C#.NET應用程序中的線程間通信問題。 希望有人會指導我關於可能的解決方案的正確方向。C#.NET線程問題

我在C#.NET中有一個應用程序。它是一個Windows窗體應用程序。 我的應用程序有兩個線程 - 一個線程是主線程(UI線程),另一個線程是子線程。讓子線程調用「workerThread」 應用程序中只有一種形式。我們稱之爲「MainForm」形式。

子窗口線程在MainForm加載時啓動(使用窗體的「Load」事件處理函數啓動線程)

在MainForm類,我有一個變量命名爲「限緊」,這是一個公共布爾變量,它作爲一個標誌,指示子線程是否應該繼續工作,還是應該停止

我有另一個類(除了MainForm類),它包含我在子線程中執行的方法。讓我們稱這個第二類爲「WorkerClass」。 我傳遞到當前書(MainForm的)的引用進入「WorkerClass」

我具有其中設置「限緊」如果點擊的,然後爲「true」的主要形式的按鈕「停止」的構造調用「workerThread.Join()」來等待子線程完成執行。

在子線程中,方法「doWork」不斷檢查內的「parentForm.stopWork」的狀態以瞭解循環。如果「stopWork」設置爲「true」,則循環會中斷,然後該方法結束。

現在,問題是,一旦我點擊「停止」按鈕,應用程序就會掛起。

我下面的代碼粘貼部位,使其更易於理解:

public partial class MainForm : Form 
{ 
    Thread workerThread = null; 
    ThreadStart workerThreadStart = null; 
    WorkerClass workerClass = null; 

    public bool stopWork = true; 

    /*.......... some code ............*/ 

    private void MainForm_Load(object sender, EventArgs e) 
    { 
     workerThreadStart = new ThreadStart(startWork); 
     workerThread = new Thread(workerThreadStart); 
     stopWork = false; 
     workerThread.Start(); 
    } 

    private void startWork() 
    { 
     workerClass = new WorkerClass(this); 
    } 

    private void buttonStop_Click(object sender, EventArgs e) //"stop" button 
    { 
     if (workerThread != null) 
     { 
      if (workerThread.IsAlive == true) 
      { 
       stopWork = true; 
       workerThread.Join(); 
      } 
     } 
    } 

    /*.......... some more code ............*/ 

} 

public class WorkerClass 
{ 
    MainForm parentForm=null; 

    /*......... some variables and code ........*/ 

    public WorkerClass(MainForm parentForm) 
    { 
     this.parentForm=parentForm; 
    } 

    /* .............. some more code ...........*/ 

    public void doWork() 
    { 
     /*.......... some variables and code ...........*/ 

     for(int i=0;i<100000;i++) 
     { 
      // ** Here is the check to see if parentForm has set stopWork to true ** 
      if(parentForm.stopWork==true) 
       break; 

      /*......... do some work in the loop ..........*/ 


     } 

    } 

    /********* and more code .........*/ 
} 

我想我可能知道問題所在。 問題出在試圖通過調用「workerThread.Join()」方法阻止父表單時訪問父表單中的「stopWork」變量的子線程中的「doWork」方法。所以,我認爲這是一個「僵局」問題。

我正確地發現問題嗎?還是我錯了,問題在於別的地方?

如果這確實是一個僵局,那麼有什麼可能的解決方案來解決這個問題?

我做了一些谷歌上搜索,發現大量的資源在線程同步以及如何避免死鎖。但我不明白如何將它們專門應用於我的問題。

我真的很感謝任何關於解決這個問題的幫助或指導。

+0

我想查看你的工作線程類的一部分,看看它是如何檢測是否已經請求了停止工作。你能包括嗎? – BugFinder 2011-04-02 11:49:35

+0

你是從線程更新GUI嗎? – 2011-04-02 15:00:15

+0

@Lasse V. Karlsen:是的,我正在從子線程更新GUI。我在富文本框控件上調用Invoke以將消息寫入UI。 – sandyiscool 2011-04-02 15:19:26

回答

3

是的,你寫的代碼是高度容易出現死鎖。 BackgroundWorker類特別容易導致這種死鎖。

該問題位於代碼中,我們無法在您的代碼片段WorkerClass中看到該代碼。你肯定會在那裏以某種方式影響UI,總是首先考慮創建線程的主要原因。您可能使用Control.Invoke()在UI線程上運行一些代碼並更新控件。也許還可以表示工作線程已完成,並且將一個按鈕的Enable屬性設置爲true。

這是死鎖的城市,這樣的代碼無法運行,直到UI線程空閒,回到抽取其消息循環。它永遠不會在你的情況下被閒置,它被困在Thread.Join()中。工作者線程無法完成,因爲UI線程不會閒置,因爲工作線程未完成,UI線程無法閒置。僵局。

BackgroundWorker也有這個問題,RunWorkerCompleted事件不能運行,除非UI線程空閒。你需要做的是而不是阻止UI線程。說起來容易做起來難,BGW可以幫助你做到這一點,因爲它在完成時運行一個事件。您可以讓此事件執行您在Thread.Join()調用之後的代碼中執行的任何操作。你需要在你的班級中有一個布爾標誌來表明你處於'等待完成'狀態。 This answer有相關的代碼。

+0

感謝您的洞察力。您確實在使用豐富的文本框控件上的Invoke將消息寫入UI! :-) – sandyiscool 2011-04-02 15:18:32

+1

我認爲,從漢斯的回答以及類似的回答中可以看出,線程難以得到正確的結論。你絕對需要完全瞭解你在做什麼,其他線程正在做什麼以及它們如何相互作用。 – 2011-04-02 21:18:10

+0

你似乎在說BackgroundWorker會讓這個問題變得更糟,而BackgroundWorker是這個問題的最終解決方案。 – MusiGenesis 2011-04-03 01:47:25

2

改爲使用BackgroundWorker代替此任務。當您想停止任務的執行時,請調用後臺工作人員的CancelAsync方法。

一般來說,滾動自己的線程代碼(在任何平臺上)是一個災難,如果你不具備多線程的一個專家級的瞭解(甚至然後它仍然是危險的)。

+0

BackgroundWorker無疑是一個可以考慮的選項。但是,如果我想用我自己的線程處理任務呢?那麼怎麼去呢? – sandyiscool 2011-04-02 12:01:21

+2

這就好像聽到有人說「打電話給消防部門肯定是一個可以考慮的選擇,但是,如果我想自己放火燒房子怎麼辦?」我建議通過這些教程的工作:http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx – MusiGenesis 2011-04-02 12:07:09

+0

大聲笑。好吧,你一定不會叫消防部門把出一個「廚房火」:-)除了非常複雜的多線程場景,我喜歡獨自處理線程。這是一個簡單的「開始線程>檢查條件>停止線程」的情況。它甚至不涉及線程訪問的任何共享資源。它只需要一個檢查一個標誌而不會造成死鎖的方法。 – sandyiscool 2011-04-02 12:15:49