多线程总结之旅(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

 

 

 

 

  多线程这一块就总结到这了,都是平时本身总结的东西,但愿对你们有用,有机会把事件和委托总结一下。

相关文章
相关标签/搜索