推 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
→ 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