本來是寫完線程池就結束多線程總結之旅系列的,但是想想平時在項目中用到線程僅僅不夠的,為什么這么說呢?舉個例子:我們有一個函數,它的功能就是加載數據,然后綁定到datagridview。現在我們開啟一個線程去執行這個函數。結果可想而知,它會報錯:提示線程無法訪問。。。之類的話。為什么報錯呢?因為你在開啟的線程中操作了datagridview控件,也就是你跨線程調用控件了。
那么我們應該怎么跨線程調用控件呢?下面我就把我總結的幾種方法奉獻給各位。
跨線程調用控件的幾種方法:
1、方法一:Control.CheckForIllegalCrossThreadCalls = false;這是通過禁止編譯器檢查對跨線程訪問操作,但是這種方法不是安全的解決辦法,盡量不要使用。
為什么說不安全呢?
(1)我們查看CheckForIllegalCrossThreadCalls 這個屬性的定義,就會發現它是一個static的,也就是說無論我們在項目的什么地方修改了這個值,他就會在全局起作用。
(2)一般對於跨線程訪問是否存在異常,我們通常都會去檢查。如果項目中其他人修改了這個屬性,那么我們的方案就失敗了。
代碼下載:http://files.cnblogs.com/files/qtiger/CheckForIllegalCrossThreadCalls.rar
2、方法二: 使用Delegate和Invoke跨線程調用控件(也叫代理方式)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Thread t = new Thread(ModifyLabelText); t.Start(); } /// <summary> /// 定義委托 /// </summary> private delegate void InvokeDelegate(); /// <summary> /// this.label1.InvokeRequired就是問問我們要不要使用代理執行ModifyLabelText方法 /// </summary> private void ModifyLabelText() { //使用Invoke代理的方式調用ModifyLabelText方法 if (this.label1.InvokeRequired) { InvokeDelegate invokeDelegate=new InvokeDelegate(ModifyLabelText); this.Invoke(invokeDelegate); } else { this.label1.Text = "我已經跨線程修改了Label的值"; } } } }
代碼下載:http://files.cnblogs.com/files/qtiger/InvokeAndDelegate.zip
3、方法三:使用BeginInvoke和Delegate的方式。(也叫代理方式)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Thread t = new Thread(ModifyLabelText); t.Start(); } /// <summary> /// 定義委托 /// </summary> private delegate void InvokeDelegate(); /// <summary> /// this.label1.InvokeRequired就是問問我們要不要使用代理執行ModifyLabelText方法 /// </summary> private void ModifyLabelText() { //使用BeginInvoke代理的方式調用ModifyLabelText方法 if (this.label1.InvokeRequired) { InvokeDelegate invokeDelegate=new InvokeDelegate(ModifyLabelText); this.BeginInvoke(invokeDelegate); } else { this.label1.Text = "我已經跨線程修改了Label的值"; } } } }
在這里進行一下說明:Invoke方法和BeginInvoke方法的區別是Invoke方法是同步的, 它會等待工作線程完成;BeginInvoke方法是異步的, 它會另起一個線程去完成工作線程。
代碼下載:http://files.cnblogs.com/files/qtiger/BeginInvokeAndDelegate.zip
4、方法四:使用BackgroundWorker組件(推薦使用這個方法)
(1)概述:BackgroundWorker是·net里用來執行多線程任務的控件,它允許編程者在一個單獨的線程上執行一些操作。耗時的操作(如下載和數據庫事務)在長時間運行時可能會導致用戶界面 (UI) 始終處於停止響應狀態。如果您需要能進行響應的用戶界面,而且面臨與這類操作相關的長時間延遲,則可以使用BackgroundWorker類方便地解決問題。
(2)工作原理:該控件有三個事件:DoWork 、ProgressChanged 和 RunWorkerCompleted。在程序中調用RunWorkerAsync方法則會啟動DoWork事件的事件處理,當在事件處理過程中,調用 ReportProgress方法則會啟動ProgressChanged事件的事件處理,而當DoWork事件處理完成時,則會觸發RunWorkerCompleted事件。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WorkerBackgrounderExmple { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private static int MaxRecords = 100; private void btnStart_Click(object sender, EventArgs e) { if (backgroundWorker1.IsBusy) { return; } this.listView1.Items.Clear(); this.backgroundWorker1.RunWorkerAsync(MaxRecords); this.btnStart.Enabled= false; this.btnCancel.Enabled= true; } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { try { e.Result = this.RetrieveData(this.backgroundWorker1, e); } catch (Exception ex) { MessageBox.Show(ex.Message); throw; } } private int RetrieveData(BackgroundWorker worker, DoWorkEventArgs e) { int maxRecords=(int)e.Argument; int percent=0; for (int i = 1; i <=maxRecords; i++) { if (worker.CancellationPending) { return i; } percent=(int)(((double)i/(double)maxRecords)*100); worker.ReportProgress(percent, new KeyValuePair<int, string>(i, Guid.NewGuid().ToString())); Thread.Sleep(100); } return maxRecords; } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { KeyValuePair<int, string> record = (KeyValuePair<int, string>)e.UserState; this.label1.Text = string.Format("There are {0} records retrieved!", record.Key); this.progressBar1.Value = e.ProgressPercentage; this.listView1.Items.Add(record.Value); } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { try { this.label1.Text = string.Format("Total records: {0}", e.Result); this.btnStart.Enabled = true; this.btnCancel.Enabled = false; } catch (TargetInvocationException ex) { MessageBox.Show(ex.InnerException.GetType().ToString()); } } private void btnCancel_Click(object sender, EventArgs e) { this.backgroundWorker1.CancelAsync(); } } }
源碼下載:http://files.cnblogs.com/files/qtiger/WorkerBackgrounderExmple.zip
多線程這一塊就總結到這了,都是平時自己總結的東西,希望對大家有用,有機會把事件和委托總結一下。
