2012-04-03 63 views
1

這可能是一個愚蠢的問題,如果這已經回答了其他地方,那麼我真的很感激,如果我的搜索沒有止跌回升有人可以點我給它任何明確的。的Thread.join在UI線程阻塞也子線程


概括地說,我的問題是,當我在已標記停止childThread一個子線程UI線程做childThread.Join()似乎要禁止以及主線程所以一切只是掛起。
由於使用Join會阻塞UI,因爲childThread在被告知退出之後應該在一秒鐘內完成,因此此刻本身並不是問題。
發生這種情況時,我正在等待運行重複進程的線程退出,然後才能運行另一種返回某些信息但不能與其他進程同時運行的方法。

我的Winforms申請通過pinvoking爲硬件中的C API用一塊USB硬件的集成。

硬件API具有將開始,將無限期地重複運行,並迅速與新的信息,然後我需要傳遞到UI回調的過程的方法。
此操作可以通過對硬件API的另一個調用取消,該硬件API設置硬件可以看到的標誌,以便它知道要退出。
我包裹着我自己的C#代碼這個C API和包裝中的我不得不分拆啓動過程調用在另一個線程,這樣的活動不會阻止用戶界面。

這裏有大約我在做什麼編輯的亮點。

public class DeviceWrapper 
{ 
    Thread childThread = null; 

    void DeviceWrapper 
    { 
     //Set the callback to be used by the StartGettingInformation() process 
     PInvokeMethods.SetGetInformationCallback(InformationAcquiredCallback); 
    } 

    public void StartProcess() 
    { 
     childThread = new Thread(new ThreadStart(GetInformationProcess)) 
     childThread.Start(); 
    } 

    void GetInformationProcess() 
    { 
     PInvokeMethods.StartGettingInformation(); 
    } 

    //This callback occurs inside the childThread 
    void InformationAcquiredCallback(Status status, IntPtr information) 
    { 
     //This callback is triggered when anything happens in the 
     //StartGettingInformation() method, such as when the information 
     //is ready to be retrieved, or when the process has been cancelled. 
     if(status == Status.InformationAcquired) 
     { 
      FireUpdateUIEvent(); 
     } 
     //If the cancel flag has been set to true this will be hit. 
     else if(status == Status.Cancelled) 
     { 
      //Reset the cancel flag so the next operation works ok 
      PInvokeMethods.SetCancelFlag(false); 

      childThread.Abort(); 
     } 
    } 

    //This method runs once, and can't run at the same time as GetInformationProcess 
    public string GetSpecificInformation() 
    { 
     //This triggers InformationAcquiredCallback with a status of Cancelled 
     StopProcess(); 

     if(childThread.IsAlive) 
     { 
      childThread.Join(); 
     } 

     return PInvokeMethods.GetSpecificInformation(); 
    } 

    public void StopProcess() 
    { 
     PInvokeMethods.SetCancelFlag(true); 
    } 
} 

使用當我打電話childThread.Join()整個應用程序嘎然而止(這是我所期待的UI,這就是罰款)和childThread也似乎停止,因爲回調從來沒有得到這個代碼再次擊中。

不過,如果我使用下面的代碼來代替:

public string GetSpecificInformation() 
{ 
    //This triggers InformationAcquiredCallback with a status of Cancelled 
    StopProcess(); 
    string s = ""; 

    ThreadPool.QueueUserWorkItem(new WaitCallback(delegate 
    { 
     if(childThread.IsAlive) 
     { 
      childThread.Join(); 
     } 
     s = PInvokeMethods.GetSpecificInformation();    
    })); 

    return s; 
} 

然後,一切都被擊中預期和childThread確實完成,一切都很好,但顯然我的字符串得到WaitCallback火災前返回空並分配到它。

所以,我只是要吮吸它,並更改類,所以我使用QueueUserWorkItem和WaitCallback和觸發事件來處理我的字符串返回?
在我的第一個方法中,是否有一些愚蠢的行爲導致childThread阻塞?
還是有另一種策略或類,我應該使用,銘記它是.NET 3.5我在?

+3

我花了近30年的時間試圖阻止使用TThread.WaitFor()和其他類似的硬鎖同步機制來產生死鎖的Delphi開發人員。就在我想到我到某個地方時,Java和C#開發人員發現了join()。永無止境的噩夢。 – 2012-04-03 16:06:18

+0

相信我,除非我絕望而且不在乎自己的深度,否則我不會觸及任何這些;)那麼你會推薦什麼呢? – Nanhydrin 2012-04-03 16:12:38

+0

如果你聽Eric和其他人的話。 Invoke,BeginInvoke等,你不會錯誤的。不要在事件處理程序中等待 - 對其他線程有話要說的代理在主線程上觸發時返回的信號採取行動。 – 2012-04-03 17:16:01

回答

5

那麼,FireUpdateUIEvent();聽起來像一種可能的方法 發佈 發送到MsgQueue(Control.Invoke())。當主線程在Join()中等待時,那麼你有一個典型的死鎖。

另外,childThread.Abort()不被認爲是安全的。

那麼,我只需要吸取它並更改類,以便使用QueueUserWorkItem和WaitCallback並觸發一個事件來處理我的字符串返回?

我當然會重新設計它。它可能可以簡化一點。

+0

FireUpdateUIEvent不會直接更新UI本身,但UI一定會爲它提供處理程序,並且該回調將阻止等待它返回。但是由於它實際上並沒有返回任何值得關注的價值,我可以想象將它轉換成另一個線程,或者我應該做的更好嗎? – Nanhydrin 2012-04-03 16:09:54

+0

@Nhydhydrin:Henk是對的;你應該重新設計整個事情。你不應該在UI線程中做線程中止或連接。如果你想告訴UI線程在另一個線程上發生了一些有趣的事情,請執行與操作系統相同的操作:將消息放入其隊列中。 – 2012-04-03 16:12:55

+0

FireUpdateUIEvent應該使用Control.BeginInvoke,而不是Control.Invoke。這可能會解決僵局。但是你仍然會有Join()和Abort(),這不是一個好的情況。 – 2012-04-03 16:12:55