1 UI線程執(zhí)行耗時(shí)操作 UI線程被阻塞 無(wú)法響應(yīng)窗體消息隊(duì)列中的其他消息。
2 非UI線程修改UI屬性 由于窗體資源也屬于臨界資源 所以有互斥訪問(wèn)的機(jī)制。
3 線程的同步問(wèn)題 線程A等待線程B執(zhí)行完畢后才能開(kāi)始執(zhí)行。
問(wèn)題1的解決方法: 解決方法只有一種,就是開(kāi)啟新線程執(zhí)行耗時(shí)操作,使原界面線程仍能夠響應(yīng)窗體消息隊(duì)列中的用戶消息及系統(tǒng)消息。
開(kāi)啟新線程的方式有以下各種: 1) 使用System.Threading.Thread類與System.Threading.ThreadStart委托或System.Threading.ParameterizedThreadStart委托來(lái)實(shí)現(xiàn)開(kāi)啟新線程。 ThreadStart委托的類型: void ThreadStart(void); ParameterizedThreadStart委托的類型: void ParameterizedThreadStart(object[]);
ThreadStart委托可以指向一個(gè)無(wú)參數(shù)無(wú)返回值的方法。 ParameterizedThreadStart委托可以指向一個(gè)有參數(shù)無(wú)返回值的方法。
Thread類實(shí)例化的時(shí)候可以向構(gòu)造函數(shù)傳入ThreadStart委托的實(shí)例或ParameterizedThreadStart委托的實(shí)例,然后使用Thread.Start()以異步方式調(diào)用一個(gè)方法。
2) 為需要異步執(zhí)行的耗時(shí)方法定義一個(gè)委托,使用該委托的實(shí)例的BeginInvoke方法來(lái)異步調(diào)用該方法,BeginInvoke方法附帶了AsyncCallback類型的回調(diào)函數(shù)委托以及object類型的參數(shù)。 然后可以在AsyncCallback類型的回調(diào)函數(shù)中使用EndInvoke方法來(lái)得到異步方法的返回值。
3) 可以使用System.Timers.Timer定時(shí)器類來(lái)實(shí)現(xiàn)在新線程中執(zhí)行耗時(shí)操作,System.Timers.Timer定時(shí)器不同于System.Windows.Forms.Timer定時(shí)器,System.Timers.Timer定時(shí)器的定時(shí)事件的響應(yīng)函數(shù)并不是在調(diào)用定時(shí)器Start方法的線程中去執(zhí)行。
4) 可以使用BackgroundWorker組件來(lái)實(shí)現(xiàn)在新線程中執(zhí)行耗時(shí)操作(通過(guò)訂閱DoWork事件).
問(wèn)題2的解決方法 以下代碼是.NetFramework 2.0類庫(kù)中避免多線程修改界面造成的臨界資源死鎖問(wèn)題的代碼。 System.Windows.Forms.Control.get_Handle方法的內(nèi)部實(shí)現(xiàn)
public IntPtr get_Handle() { if ((checkForIllegalCrossThreadCalls && !inCrossThreadSafeCall) && this.InvokeRequired) { throw new InvalidOperationException(SR.GetString("IllegalCrossThreadCall", new object[] { this.Name })); } if (!this.IsHandleCreated) { this.CreateHandle(); } return this.HandleInternal; }
在修改每個(gè)控件的屬性的時(shí)候,都會(huì)先調(diào)用get_Handle方法獲取一個(gè)操作句柄,在該方法內(nèi)部會(huì)判斷Control類的靜態(tài)成員CheckForIllegalCrossThreadCalls的值(該成員用來(lái)表示是否啟用安全模式,安全模式的意思就是禁止跨線程修改界面屬性來(lái)避免多線程訪問(wèn)臨界資源死鎖的問(wèn)題),第二個(gè)判斷的屬性是InvokeRequired屬性(該屬性用來(lái)表示當(dāng)前方法是否是在跨線程調(diào)用)。 所以我們可以通過(guò)修改CheckForIllegalCrossThreadCalls屬性為False來(lái)關(guān)閉安全模式,但有可能造成線程死鎖問(wèn)題。
解決方法只有兩個(gè) 1) 設(shè)置CheckForIllegalCrossThreadCalls屬性為False,關(guān)閉.net的安全模式,在對(duì)界面屬性修改的代碼加上lock,來(lái)實(shí)現(xiàn)同一時(shí)間僅有一個(gè)線程修改界面屬性。 2) 在設(shè)置界面屬性的方法中詢問(wèn)InvokeRequired屬性,如果是非界面線程修改界面屬性,則讓界面線程來(lái)調(diào)用設(shè)置界面屬性的方法。(這個(gè)方法是MSDN實(shí)例中慣用的方法,也是BackgroundWorker等組件的內(nèi)部實(shí)現(xiàn)方式)
推薦大家使用BackgroundWorker來(lái)實(shí)現(xiàn)耗時(shí)操作的輔助線程以及跨線程修改界面屬性等操作,關(guān)于BackgroundWorker的具體使用方法見(jiàn) 一日一練 之 BackgroundWorker
|