多线程总结之旅(12):跨线程调用控件的几种方式
本來是寫完線程池就結束多線程總結之旅系列的,但是想想平時在項目中用到線程僅僅不夠的,為什么這么說呢?舉個例子:我們有一個函數,它的功能就是加載數據,然后綁定到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事件。
您必須非常小心,確保在 DoWork 事件處理程序中不操作任何用戶界面對象(否則仍會停止響應)。而應該通過 ProgressChanged和 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
?
?
?
?
多線程這一塊就總結到這了,都是平時自己總結的東西,希望對大家有用,有機會把事件和委托總結一下。
轉載于:https://www.cnblogs.com/qtiger/p/5834320.html
總結
以上是生活随笔為你收集整理的多线程总结之旅(12):跨线程调用控件的几种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu开启SSHD服务
- 下一篇: EPPLUS之外的选择,EXCEL的操作