看板 C_Sharp 關於我們 聯絡資訊
最近負責開發一個dll,裡面包含了一個UserControl(以下簡稱 UC) 這個UC含有許多功能,所以,UC有錯誤時,希望能夠透過本身的介面顯示出來。 因此這個UC會有一個Rirchtextbox 來顯示UC的log並寫成file。 另一位朋友,則是負責開發Form,並把我的UC 加入到他的Form。 但問題發生了,當他將我的UC初始化完成後,Add UC到他的Form。 系統卻拋出跨執行緒處理異常的錯誤 ==> 如右圖 http://i.imgur.com/BlIKUOm.jpg
我和朋友嘗試的許多方式,還是會出現錯誤。而且,如果執行 Richtextbox.Text = "aaa"; ==> 不會出現錯誤 Richtextbox.AppendText("aaa"); ==> 拋出跨執行緒錯誤 嘗試使用RichtextBox和TextBox 都是相同錯誤。... 附上簡單寫的Code (Mega空間) => https://4fun.tw/IDMo 原始路徑: https://mega.nz/#!6AoxHTAJ!DWJmWJhT9t7NhesNizTZVPZawbrByImnVM2h_eZn87k 請教一下各位前輩,到底是什麼原因造成的呢? 有什麼解決方式呢?? 謝謝 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 114.27.132.126 ※ 文章網址: https://www.ptt.cc/bbs/C_Sharp/M.1459174541.A.A5C.html
yeo1987: 你程式中有兩個Thread,一個是程式啟動時UI的主Thread, 03/28 22:56
yeo1987: 另一是每次Click時產生的新的Thread,你把UserControl建 03/28 22:56
yeo1987: 立在新的Thread中,卻用主Thread去Invoke,就跨執行續了 03/28 22:57
don750421: 感謝yeo解惑,這個問題我也有詢問過我朋友.. 03/28 23:02
don750421: 他的Form會引用不同模組,每個模組都是建立不同的 03/28 23:03
don750421: Thread去處理,所以,建立UC和呼叫UC的不一定是同一個 03/28 23:03
don750421: Thread,如果是這樣的話,有甚麼辦法解決呢? 03/28 23:04
※ 編輯: don750421 (114.27.132.126), 03/28/2016 23:05:00
yeo1987: Control.Invoke是以該物件所屬的執行續執行委派,因此, 03/28 23:18
yeo1987: 只要UserControl是在主執行續下建立,執行流程中跨執行續 03/28 23:20
yeo1987: 時,需要涉及UI,使用UserControl.Invoke就可以了。 03/28 23:20
yeo1987: 其實因為你負責開發UserControl,你只要保證操作UI時是在 03/28 23:24
yeo1987: UserControl所屬的執行續下執行。 03/28 23:25
yeo1987: 發現緒一直打錯... - - 03/28 23:29
don750421: 請教一下,您指的"只要UserControl是在主執行續下建立" 03/28 23:39
don750421: 是指Form的主執行緒?還是UC的主執行緒? 03/28 23:40
don750421: 如果是Form的主緒,有和開發Form的人員討論過.. 03/28 23:41
don750421: 因為Form的主緒還會去呼叫其他的Thread處理事情... 03/28 23:42
don750421: 如果拿Form的主緒呼叫我的UC,則畫面會有停頓的情況... 03/28 23:42
yeo1987: 以上是指同一個,Multi UI Thread我想不是你要問的問題… 03/28 23:43
yeo1987: 我指的是"建立"與"操作UI"時,使用主執行續呼叫。 03/28 23:44
yeo1987: 如果你開一個新的執行續,裡面的工作卻是不停更新UI,自 03/28 23:45
yeo1987: 然會卡。 03/28 23:46
※ 編輯: don750421 (114.27.132.126), 03/28/2016 23:50:51
don750421: 因為之前顯示Log的方式,是使用DataGridview,每一筆 03/28 23:54
don750421: Log就只需要datagridview.rows.add("xxx")加入 03/28 23:55
don750421: 想說換成TextBox簡單一些,但是開發Form的就說,之前 03/28 23:55
don750421: 呼叫方式也沒變,為什麼換成textbox就不行... 03/28 23:56
don750421: 不然,我也想說,明明我自己寫Log也都正常啊 = =||| 03/28 23:56
don750421: 和開發Form的討論過,主Thread不能拿來new 我的UC 03/28 23:57
don750421: 所以,在Sample才會new Thread 來模擬現有的情況... 03/28 23:58
don750421: 而且,UC並非只有顯示Log而已,還有其他的功能.. 03/29 00:04
don750421: 我這邊只有濃縮有問題的部分寫成Sample.. 03/29 00:04
don750421: 所以,除了透過主Thread建立我的UC外,還有其他方式嗎? 03/29 00:05
yeo1987: 這樣的要求... 那你在Contructor內不要呼叫Log操作UI, 03/29 00:21
yeo1987: 並且在公開呼叫的方法內,操作UI的部分都要檢查是否需要 03/29 00:21
yeo1987: Invoke 03/29 00:21
yeo1987: Constructor -.-,BTW,這樣的做法真的不推薦... 03/29 00:26
Litfal: 我說,你們是在把事情搞複雜...... 03/29 03:19
yeo1987: L大的解法會是?想學習 03/29 08:06
Litfal: 我是說原PO和他朋友,這種比較複雜的需求應該把系統邊界定 03/29 16:16
Litfal: 好,中間的操作介面也定義出來。 03/29 16:17
yeo1987: 認同L大,說真的原PO若堅持要在不同執行緒下操作UI,WIN 03/29 18:59
yeo1987: Form中是有Control.CheckForIllegalCrossThreadCalls可以 03/29 18:59
yeo1987: 攔截錯誤,但是這樣寫出來的程式,沒問題就沒問題,出問 03/29 19:00
yeo1987: 題時很難找到問題點。 03/29 19:01
don750421: 感謝兩位前輩回覆,今天詢問朋友的結果... 03/29 23:41
don750421: 朋友的Form介面跟我提供的Sample雷同,會有許多TabPage 03/29 23:42
don750421: 而他的TabPage是以他的MainThread來初始化... 03/29 23:42
don750421: 但是,因為我的是引用的部分,所以會是另外一個Thread 03/29 23:43
don750421: 如果都使用MainThread,變得需要先長我的TabPage,在長 03/29 23:43
don750421: 他的,這樣在畫面上會造成一些些的延遲,反之,如果先 03/29 23:44
don750421: 建他的TabPage,最後跑到我的UC時,也會稍微有一些些 03/29 23:44
don750421: 延遲的感覺... 03/29 23:45
don750421: 而且,因為我的dll是使用動態呼叫,也等於說,不一定在 03/29 23:46
don750421: 每一個場合都需要引用我的UC,所以才會另起一個Thread 03/29 23:46
don750421: 當初討論需求時,只有提到寫功能需求及傳入的參數... 03/29 23:47
don750421: SO...這部分還在想有啥其他解法... 03/29 23:47
Litfal: 你的UC一定不是單純的UI,包含了很多耗時作業 03/30 02:44
Litfal: 基本上,WinForm不會違反Control就是由UI執行續建立與操作 03/30 02:44
Litfal: 這個原則,否則會遇到很多麻煩。 03/30 02:45
Litfal: 你要先把UI單純化:只是顯示資料與發起作業,把業務邏輯提 03/30 02:47
Litfal: 到另外的類別裏,在那裡要開幾個線程隨便你。 03/30 02:48
Litfal: 而不是希望建立新的的Thread來控制Control,並希望該控制 03/30 02:49
Litfal: 項的工作都由這個Thread完成。 03/30 02:49
Litfal: btw,如果你是遇到UI更新頻率太高(如LOG太多)而卡死的問題 03/30 02:51
Litfal: 那是需要別的手段優化。想用TextBox直接顯示LOG MESSAGE 03/30 02:52
Litfal: 那個串接起來的字串長度會蠻可怕的。 03/30 02:52
cid1979: http://goo.gl/jJMqJ1 關鍵字.net cross thread delegate 03/30 05:32
don750421: 我的UC很單純,屬於被動元件,Form引用dll,UC的任何 03/30 23:23
don750421: 作都是由public的Method所觸發,像是其中一個功能就是 03/30 23:24
don750421: Form呼叫UC,透過WebService抓取檔案,再透過UC呈現.. 03/30 23:25
don750421: 沒有Hardcode任何流程,或是引用其他dll.. 03/30 23:26
don750421: 至於L大提到的TextBox處理,我個人是有限制行數,超過 03/30 23:27
don750421: 2000就從前面逐行刪除,應該不至於有您所提到的問題@@ 03/30 23:27
don750421: 不過,另我好奇的是,今天嘗試使用其他物件來顯示Log.. 03/30 23:28
don750421: ListBox和datagridview不會跳出跨執行緒的錯誤,為什麼 03/30 23:29
don750421: 難道是這兩種物件背後有特別做甚麼手腳嗎?? 03/30 23:29
Litfal: C#的string是immutable,如果你認為重串那兩千行不會造成 03/30 23:38
Litfal: 額外開銷... 03/30 23:39
Litfal: 如果你是把呼叫WebService的細節直接寫在UC裡面,這就是 03/30 23:41
Litfal: 把業務邏輯寫在UC裡面。不過先不討論"寫在哪裡" 03/30 23:43
Litfal: 你要全部透過UC的public method控制也沒關係,但流程應該 03/30 23:44
Litfal: 是:uc method-> service method 03/30 23:46
Litfal: service method done -> event -> UC ->update UI 03/30 23:47
Litfal: service method裡面可以用非同步去做,這樣UI與其執行續就 03/30 23:48
Litfal: 只負責發起工作與顯示資料,而不會被業務邏輯工作佔用 03/30 23:49
Litfal: 既可以優化用戶體驗,也沒有必須要用其他執行續去建控制項 03/30 23:50
yeo1987: 跨執行緒操作UI沒有跳出錯誤不代表你的程式是執行緒安全 03/31 00:28
yeo1987: 的,沒處理好這塊,會有可能發生意料之外的錯誤…你程式 03/31 00:28
yeo1987: 中公開的方法不需考慮被呼叫時是使用哪一個執行緒,甚至 03/31 00:28
yeo1987: 你在方法內要再開幾個執行緒去抓資料都可以,同步、非同 03/31 00:28
yeo1987: 步都可以;但在更新UI時,請回到UserControl所屬的直行 03/31 00:28
yeo1987: 緒叫用。 03/31 00:28