首頁  >  文章  >  後端開發  >  從0自學C#02--子線程訪問主線程(UI線程)控件

從0自學C#02--子線程訪問主線程(UI線程)控件

黄舟
黄舟原創
2017-02-04 10:33:242308瀏覽

如果使用多執行緒處理來提高 Windows 窗體應用程式的效能,則你必須確保以執行緒安全的方式呼叫控制項。

存取 Windows 窗體控制項不是本身就執行緒安全的。如果有兩個或兩個以上執行緒操作控制項的狀態,則可能迫使該控制項處於不一致狀態。可能出現其他與執行緒相關的 bug,例如爭用條件和死鎖。請務必確保以線程安全的方式存取控制項。

1.初學者常常遇到的問題

從未使用 Invoke 方法創建控制項的執行緒呼叫控制項是不安全的。下面是一個非線程安全的呼叫範例。執行時間會引發 InvalidOperationException 訊息,報錯「從並未建立該控制項的執行緒存取該控制項 控制項名稱」。

// This event handler creates a thread that calls a // Windows Forms control in an unsafe way.private void setTextUnsafeBtn_Click(    object sender, 
    EventArgs e)
{    this.demoThread = 
        new Thread(new ThreadStart(this.ThreadProcUnsafe));    this.demoThread.Start();
}// This method is executed on the worker thread and makes// an unsafe call on the TextBox control.private void ThreadProcUnsafe()
{    this.textBox1.Text = "This text was set unsafely.";
}


2.解決方法

如需對 Windows 窗體控制進行執行緒安全的呼叫。

①查詢控制項的 InvokeRequired 屬性。

②若 InvokeRequired 傳回 true,則用實際呼叫控制項的委託來呼叫 Invoke。

③若 InvokeRequired 傳回 false,則請直接呼叫控制項。

這裡分同步執行委託和非同步執行委託。

從0自學C#02--子線程訪問主線程(UI線程)控件

在以下程式碼範例中,在 ThreadProcSafe 方法中實作了執行緒安全的調用,該方法由後台執行緒執行。若 TextBox 控制項的 InvokeRequired 傳回 true,則 ThreadProcSafe 方法建立一個 SetTextCallback 實例並將其傳遞到窗體的 Invoke 方法。這導致在創建了 SetText 控制項的執行緒上呼叫 TextBox 方法,並且在該執行緒上下文中直接設定 Text 屬性。

// This event handler creates a thread that calls a 
// Windows Forms control in a thread-safe way.
private void setTextSafeBtn_Click(    
object sender, 
    EventArgs e)
{    this.demoThread = 
        new Thread(new ThreadStart(this.ThreadProcSafe));    
this.demoThread.Start();
}// This method is executed on the worker thread and makes
// a thread-safe call on the TextBox control.
private void ThreadProcSafe()
{    this.SetText("This text was set safely.");
}// This delegate enables asynchronous calls for setting
// the text property on a TextBox control.delegate void SetTextCallback(string text); 
// This method demonstrates a pattern for making thread-safe
// calls on a Windows Forms control. 
//
// If the calling thread is different from the thread that
// created the TextBox control, this method creates a
// SetTextCallback and calls itself asynchronously using the// Invoke method.
//
// If the calling thread is the same as the thread that created
// the TextBox control, the Text property is set directly. private void SetText(string text)
{    // InvokeRequired required compares the thread ID of the
    // calling thread to the thread ID of the creating thread.
    // If these threads are different, it returns true.
    //this.textBox1.InvokeRequired will be replaced by
    //this.InvokeRequired, if want to set many controls' 
    //attribute or text.
    if (this.textBox1.InvokeRequired)// or this.InvokeRequired
    {   
        SetTextCallback d = new SetTextCallback(SetText);        
this.Invoke(d, new object[] { text });
    }    else
    {        this.textBox1.Text = text;
    }
}

3.BackgroundWorker元件

在應用程式中實作多執行緒的首選方式是使用 BackgroundWorker 元件。 BackgroundWorker 元件為多執行緒處理使用事件驅動模型。後台執行緒運行你的 DoWork 事件處理程序,創建了你的控制項的執行緒運行 ProgressChanged 和 RunWorkerCompleted 事件處理程序。你可以從 ProgressChanged 和 RunWorkerCompleted 事件處理器中呼叫控制項。

①創建一種方法來進行你想在後台執行緒中進行的工作。不要呼叫由此方法中的主執行緒所建立的控制項。

②建立一種方法來報告後台工作結束後的後台工作結果。 在此方法中可以呼叫主執行緒所建立的控制項。

③將步驟 1 中建立的方法綁定到 DoWork 實例中的 BackgroundWorker 事件,並將步驟 2 中建立的方法綁定到相同實例的 RunWorkerCompleted 事件。

④若要啟動後台線程,請呼叫 RunWorkerAsync 實例的 BackgroundWorker 方法。

在以下程式碼範例中,DoWork 事件處理程序使用 Sleep 來模擬需要花費一些時間的工作。它不會呼叫該窗體的 TextBox 控制項。 TextBox 控制項的 Text 屬性直接在 RunWorkerCompleted 事件處理程序中設定。

// This BackgroundWorker is used to demonstrate the 
// preferred way of performing asynchronous operations.private BackgroundWorker backgroundWorker1; 
// This event handler starts the form's // BackgroundWorker by calling RunWorkerAsync.
//
// The Text property of the TextBox control is set
// when the BackgroundWorker raises the RunWorkerCompleted
// event.private void setTextBackgroundWorkerBtn_Click(    
object sender, 
    EventArgs e)
{    this.backgroundWorker1.RunWorkerAsync();
}// This event handler sets the Text property of the TextBox
// control. It is called on the thread that created the 
// TextBox control, so the call is thread-safe.
//
// BackgroundWorker is the preferred way to perform asynchronous
// operations.private void backgroundWorker1_RunWorkerCompleted(    
object sender, 
    RunWorkerCompletedEventArgs e)
{    this.textBox1.Text = 
        "This text was set safely by BackgroundWorker.";
}

也可透過使用 ProgressChanged 事件來報告後台任務的進度。如需包含該事件的範例,請參閱 BackgroundWorker。

以上就是 從0自學C#02--子執行緒存取主執行緒(UI執行緒)控制項的內容,更多相關內容請關注PHP中文網(www.php.cn)!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn