原本是寫完線程池就結束多線程總結之旅系列的,可是想一想平時在項目中用到線程僅僅不夠的,爲何這麼說呢?舉個例子:咱們有一個函數,它的功能就是加載數據,而後綁定到datagridview。如今咱們開啓一個線程去執行這個函數。結果可想而知,它會報錯:提示線程沒法訪問。。。之類的話。爲何報錯呢?由於你在開啓的線程中操做了datagridview控件,也就是你跨線程調用控件了。 數據庫
那麼咱們應該怎麼跨線程調用控件呢?下面我就把我總結的幾種方法奉獻給各位。編程
跨線程調用控件的幾種方法:安全
一、方法一:Control.CheckForIllegalCrossThreadCalls = false;這是經過禁止編譯器檢查對跨線程訪問操做,可是這種方法不是安全的解決辦法,儘可能不要使用。多線程
爲何說不安全呢?異步
(1)咱們查看CheckForIllegalCrossThreadCalls 這個屬性的定義,就會發現它是一個static的,也就是說不管咱們在項目的什麼地方修改了這個值,他就會在全局起做用。函數
(2)通常對於跨線程訪問是否存在異常,咱們一般都會去檢查。若是項目中其餘人修改了這個屬性,那麼咱們的方案就失敗了。ui
代碼下載:http://files.cnblogs.com/files/qtiger/CheckForIllegalCrossThreadCalls.rarthis
二、方法二: 使用Delegate和Invoke跨線程調用控件(也叫代理方式)spa
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線程
三、方法三:使用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
四、方法四:使用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
多線程這一塊就總結到這了,都是平時本身總結的東西,但願對你們有用,有機會把事件和委託總結一下。