之前有寫過一篇關於如何將執行緒運算結果顯示於人機介面上?,

今天剛好找到一個不錯的範例How to: Make Thread-Safe Calls to Windows Forms Controls,

Let’s cut to the chase and get started!

======================================================================

Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible as well, including race conditions and deadlocks. It is important to ensure that access to your controls is done in a thread-safe way.

更新控制元件與threrad safe觀念: 如果有兩個執行緒, 一個UI(主執行緒), 另一個或更多個使用者開的執行緒要更新控制元件上(windows Form, TextBox, Label,…), 有可能造成不相容狀況

"Control control name accessed from a thread other than the thread it was created on."

如果在使用者開的執行緒中呼叫UI控制元件, 會出現上面這個錯誤提示

The following code example shows how to call Windows Forms controls in a thread-safe manner and not in a thread-safe manner from a worker thread. It shows a way of setting the Text property of a TextBox control in a manner that is not thread safe, and it shows two thread-safe ways of setting the Text property.

利用一種not thread safe(1)和兩種thread-safe(2)(3)方法詮釋如何設定TextBox的Text屬性

======================================================================

image

人機介面上元件名稱分別為: textbox1, setTextUnsafeBtn, setTextSafeBtn, setTextBackgroundWorkerBtn

image

第一種: unsafe call

   1: // This event handler creates a thread that calls a 
   2: // Windows Forms control in an unsafe way.
   3: private void setTextUnsafeBtn_Click(object sender, EventArgs e)
   4: {
   5:     this.demoThread =
   6:         new Thread(new ThreadStart(this.ThreadProcUnsafe));
   7:  
   8:     this.demoThread.Start();
   9: }
  10: // This method is executed on the worker thread and makes
  11: // an unsafe call on the TextBox control.
  12: private void ThreadProcUnsafe()
  13: {
  14:     this.textBox1.Text = "This text was set unsafely.";
  15: }
直接跳錯誤: 理由就是上面提到的跨執行緒的問題
"Control control name accessed from a thread other than the thread it was created on."
image

=================================================================

第二種 safe call
   1: // This method is executed on the worker thread and makes
   2:    // an unsafe call on the TextBox control.
   3:    private void ThreadProcUnsafe()
   4:    {
   5:        this.textBox1.Text = "This text was set unsafely.";
   6:    }
   7:    //----------------------------------------------------------------------
   8:    private void setTextSafeBtn_Click(object sender, EventArgs e)
   9:    {
  10:        this.demoThread =
  11:            new Thread(new ThreadStart(this.ThreadProcSafe));
  12:  
  13:        this.demoThread.Start();
  14:    }
  15:    // This method is executed on the worker thread and makes
  16:    // a thread-safe call on the TextBox control.
  17:    private void ThreadProcSafe()
  18:    {
  19:        this.SetText("This text was set safely.");
  20:    }
  21:    // This method demonstrates a pattern for making thread-safe
  22:    // calls on a Windows Forms control. 
  23:    //
  24:    // If the calling thread is different from the thread that
  25:    // created the TextBox control, this method creates a
  26:    // SetTextCallback and calls itself asynchronously using the
  27:    // Invoke method.
  28:    //
  29:    // If the calling thread is the same as the thread that created
  30:    // the TextBox control, the Text property is set directly. 
  31:  
  32:    private void SetText(string text)
  33:    {
  34:        // InvokeRequired required compares the thread ID of the
  35:        // calling thread to the thread ID of the creating thread.
  36:        // If these threads are different, it returns true.
  37:        if (this.textBox1.InvokeRequired)  // (1) 跨執行緒
  38:        {
  39:            SetTextCallback d = new SetTextCallback(SetText);
  40:            this.Invoke(d, new object[] { text });
  41:        }
  42:        else                                // (2) 無跨執行緒 
  43:        {
  44:            this.textBox1.Text = text;
  45:        }
  46:    }
   1: SetTextCallback d = new SetTextCallback(SetText);
   2: this.Invoke(d, new object[] { text });

等效於下列語法, 比較簡潔

   1: BeginInvoke(new SetTextCallback(SetText), new object[]{text});
   2:  

或是更簡潔更容易理解的

   1: BeginInvoke((Action)(()=>{ SetText(text); }));

InvokeRequired

Gets a value indicating whether the caller must call an invoke method when making method calls to the control because the caller is on a different thread than the one the control was created on.

取得一個值。這個值會指示是否由於呼叫端是在建立控制項之執行緒以外的執行緒,因此在進行控制項的方法呼叫時,應呼叫叫用 (Invoke) 方法。

控制項會繫結到特定的執行緒,而且不是安全執行緒 (Thread-Safe)。

如果您從不同的執行緒(使用者新增一條執行緒)呼叫方法,則必須使用方法來封送處理對適當執行緒的呼叫

這個屬性可以用來判斷是否一定要呼叫叫用 (Invoke) 方法,此一功能在您無法確知那一個執行緒擁有控制項時,就顯得十分好用。

=================================================================

第三種 background worker

   1: public Form1()
   2: {
   3:     InitializeComponent();
   4:     this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
   5:     this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
   6: }


   1: private void setTextBackgroundWorkerBtn_Click(object sender, EventArgs e)
   2:         {
   3:             this.backgroundWorker1.RunWorkerAsync();
   4:         }
   5:         // This event handler sets the Text property of the TextBox
   6:         // control. It is called on the thread that created the 
   7:         // TextBox control, so the call is thread-safe.
   8:         //
   9:         // BackgroundWorker is the preferred way to perform asynchronous
  10:         // operations.
  11:  
  12:         private void backgroundWorker1_RunWorkerCompleted(
  13:             object sender,
  14:             RunWorkerCompletedEventArgs e)
  15:         {
  16:             this.textBox1.Text =
  17:                 "This text was set safely by BackgroundWorker.";
  18:         }



參考來源:

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 me1237guy 的頭像
    me1237guy

    天天向上

    me1237guy 發表在 痞客邦 留言(0) 人氣()