多線程總結之旅(12):跨線程調用控件的幾種方式

  原本是寫完線程池就結束多線程總結之旅系列的,可是想一想平時在項目中用到線程僅僅不夠的,爲何這麼說呢?舉個例子:咱們有一個函數,它的功能就是加載數據,而後綁定到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事件。

您必須很是當心,確保在 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

 

 

 

 

  多線程這一塊就總結到這了,都是平時本身總結的東西,但願對你們有用,有機會把事件和委託總結一下。

相關文章
相關標籤/搜索