2016年4月11日 星期一

Cross-thread operation not valid on Windows Form Control

Windows Forms Controls 的存取不是安全執行緒(Thread-Safe)。 如果有兩個以上的執行緒在管理 Control 狀態,則可能會造成 Control 進入不一致的狀態, 也可能發生其他與執行緒相關的錯誤 - 例如競爭情況 (race conditions) 和死結 (deadlock)。 因此,必須確定 Control 存取是以Thread-Safe的方式執行。

最常發生的錯誤情況是,建立 Control 的執行緒 (i.e., Windows Form, creating thread) 與 存取Control 的執行緒 (i.e., 某個calling thread) 並不是同一個Thread,而造成 Cross-thread operation not valid 錯誤。此時,使用 Invoke method 可以解決這個問題。
(** 所謂的 Invoke method,其實只是把 delegate 與 input arguments 傳進去, 讓  Windows Form 去執行此 delegate。 參閱: Control.Invoke方法)

對 Windows Forms Controls 進行Thread-Safe 存取的作法如下:
  1. 查詢 Controls 的 InvokeRequired 屬性。( InvokeRequired 用來 compares the thread ID )
  2. 如果 InvokeRequired 回傳 true ,不同 thread 要使用 Invoke;
  3. 不然的話,相同thread 則直接存取Control 。 
[例如]:
private delegate void SetTextDelegate(string text, Color color, Font font);

private void SetText(string text, Color color, Font font)
{
                 if (InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(SetText);
                    Invoke(d, new object[] { text, color, font });
                }
                else // use the RichTextBox control directly
                {
                    txtLogging.AppendText(text);
                    txtLogging.SelectionColor = color;
                    txtLogging.SelectionFont = font;
                 }
}


利用VS 2015執行應用程式時,如果VS 2015引發 InvalidOperationException,並且會有訊息:"Control 'control name' accessed from a thread other than the thread it was created on",這就表示此 Control 正在被 none Thread-Safe 的行為存取, 此時必須用Invoke method 來解決。


See the following Q&A (from https://bbs.csdn.net/topics/390581976)
1. 為什麼更新某個 Textbox 的狀態, 不能夠直接寫 Textbox="123" 就好了?
  • 為了Thread-Safe 。微軟建議你在創建 Control 的那個 Thread 裡面去更新 Control 。如果只是 Textbox="123", CLR怎麼知道你是想做 Thread-Safe 的操作? 還是 none Thread-Safe 的操作呢?

 

2. 為什麼一定要搞個 delegate 要我們去寫的這麼複雜?
  • delegate 與 multi-thread 更新 Control 沒有什麼必然關係, 它只是指定了你更新 Control 的時候執行的具體動作

沒有留言:

張貼留言