zoukankan      html  css  js  c++  java
  • C#多线程应用:子线程更新主窗体控件的值(二)

    在上篇文章中,我已经给大家列了一个在主线程中实现的方式,这篇文章来给大家说说使用Invoke的方式的例子:

    对于不代理不太熟悉的朋友,建议先查查相关资料;

    例子一:

    在C#中,直接在子线程中对窗体上的控件操作是会出现异常,这是由于子线程和运行窗体的线程是不同的空间,因此想要在子线程来操作窗体上的控件,是不可能简单的通过控件对象名来操作,但不是说不能进行操作,微软提供了Invoke的方法,其作用就是让子线程告诉窗体线程来完成相应的控件操作。

    要实现该功能,有两种方法可以选择:

     

    1、在程序初始化的时候对要操作的控件设置下面的属性:

    System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls  =  false;

    这样,系统就不会再抛出上面所说的这个错误了。

    从实质上说,该方法是通过采用取消线程安全保护模式的方式实现的,所以不建议采用。

     

    2、基本思路如下:

    把想对另一线程中的控件实施的操作放到一个函数中,然后使用delegate代理那个函数,并且在那个函数中加入一个判断,用InvokeRequired 来判断调用这个函数的线程是否和控件线程处于同一线程中,如果是则直接执行对控件的操作,否则利用该控件的Invoke或BeginInvoke方法来执行这个代理。示例代码如下:

    复制代码
      1 using System;
      2 using System.Collections.Generic;
      3 using System.Windows.Forms;
      4 
      5 using System.Threading;
      6 
      7 namespace 子线程操作主线程窗体上的控件
      8 {
      9     public partial class frmMain : Form
     10     {
     11         /***************************************************** 定义该类的私有成员 ****************************************************/
     12         
     13         /// <summary>
     14         /// 定义一个队列,用于记录用户创建的线程
     15         /// 以便在窗体关闭的时候关闭所有用于创建的线程
     16         /// </summary>
     17         private List<Thread> ChaosThreadList;
     18 
     19         /***************************************************** 该类的初始化相关函数 ****************************************************/
     20                 
     21         /// <summary>
     22         /// 窗体的初始化函数,初始化线程队列ChaosThreadList
     23         /// </summary>
     24         public frmMain()
     25         {
     26             InitializeComponent();
     27             ChaosThreadList = new List<Thread>();
     28         }
     29                
     30         /// <summary>
     31         /// 窗体的关闭事件处理函数,在该事件中将之前创建的线程全部终止
     32         /// </summary>
     33         /// <param name="sender"></param>
     34         /// <param name="e"></param>
     35         private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
     36         {
     37             if (ChaosThreadList.Count > 0)
     38             {
     39                 //编列自定义队列,将所有线程终止
     40                 foreach (Thread tWorkingThread in ChaosThreadList)
     41                 {
     42                     tWorkingThread.Abort();
     43                 }
     44             }
     45         }        
     46 
     47         /***************************************************** 定义该类的自定义函数 ****************************************************/
     48 
     49         /// <summary>
     50         /// 定义一个代理
     51         /// </summary>
     52         /// <param name="index"></param>
     53         /// <param name="MSG"></param>
     54         private delegate void DispMSGDelegate(int index,string MSG);
     55 
     56         /// <summary>
     57         /// 定义一个函数,用于向窗体上的ListView控件添加内容
     58         /// </summary>
     59         /// <param name="iIndex"></param>
     60         /// <param name="strMsg"></param>
     61         private void DispMsg(int iIndex,string strMsg)
     62         {
     63             if (this.lstMain.InvokeRequired==false)                      //如果调用该函数的线程和控件lstMain位于同一个线程内
     64             {
     65                 //直接将内容添加到窗体的控件上
     66                 ListViewItem lvi = new ListViewItem();
     67                 lvi.SubItems[0].Text = iIndex.ToString();
     68                 lvi.SubItems.Add(strMsg);
     69                 this.lstMain.Items.Insert(0, lvi);
     70             }
     71             else                                                        //如果调用该函数的线程和控件lstMain不在同一个线程
     72             {
     73                 //通过使用Invoke的方法,让子线程告诉窗体线程来完成相应的控件操作
     74                 DispMSGDelegate DMSGD = new DispMSGDelegate(DispMsg);
     75 
     76                 //使用控件lstMain的Invoke方法执行DMSGD代理(其类型是DispMSGDelegate)
     77                 this.lstMain.Invoke(DMSGD, iIndex, strMsg);
     78                 
     79             }
     80         }
     81 
     82         /// <summary>
     83         /// 定义一个线程函数,用于循环向列表中添加数据
     84         /// </summary>
     85         private void Thread_DisplayMSG()
     86         {
     87             for (int i = 0; i < 10000; i++)
     88             {
     89                 DispMsg(i + 1, "Welcome you : " + (i + 1).ToString());
     90                 Thread.Sleep(10);
     91             }
     92         }
     93 
     94         /***************************************************** 定义该类的事件处理函数 ****************************************************/
     95 
     96         /// <summary>
     97         /// 【开始】按钮的单击事件处理函数,新建一个线程向窗体上的ListView控件填写内容
     98         /// </summary>
     99         /// <param name="sender"></param>
    100         /// <param name="e"></param>
    101         private void btnBegin_Click(object sender, EventArgs e)
    102         {
    103             //创建一个新的线程
    104             Thread tWorkingThread = new Thread(Thread_DisplayMSG);
    105 
    106             //将新建的线程加入到自定义线程队列中,以便在窗体结束时关闭所有的线程
    107             ChaosThreadList.Add(tWorkingThread);
    108 
    109             //开启线程
    110             tWorkingThread.Start();
    111         }     
    112 
    113     }
    114 }
    复制代码

     

     

    这样子就可以实现用子线程去操作主线程窗体上的控件的内容,同时,又不影响主线程对窗体上其他控件的响应。程序运行截图如下:

     

     

     

     点击[开始]按钮,程序开启一个新的线程,不断向列表中添加新的数据,而同时不会影响主界面对其它控件(例如:文本框)的响应。

     

    [P.S]:

    INVOKE方法的作用:

    它使该控件所在的线程执行Invoke方法参数中指定的代理,也就是使主线程执行我们想对控件进行的操作。

    出处:http://www.cnblogs.com/chaosimple/archive/2012/05/11/2495811.html

    --------------------------------------------------------------------------------------------------------------------

    例子二:

    本文将详细介绍C#利用子线程如何刷新主线程,需要了解更多的朋友可以参考下
    要求:如下图,使用线程操作
    1、实时显示当前时间
    2、输入加数和被加数,自动出现结果

    分析:两个问题解决的方式相同,使用子线程进行时间操作和加法操作,然后刷新主线程的控件显示结果
    复制代码 代码如下:
    using System;
    using System.Threading;
    using System.Windows.Forms;
    namespace WinThread
    {
    public partial class frmMain : Form
    {
    public frmMain()
    {
    InitializeComponent();
    }
    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void frmMain_Load(object sender, EventArgs e)
    {
    // 控件初始化
    this.txtOne.Text = "0";
    this.txtSecond.Text = "0";
    // 显示时间操作
    Thread showTimeThread = new Thread(new ThreadStart(GetTime));
    showTimeThread.IsBackground = true;
    showTimeThread.Start();
    // 加法操作
    Thread addThread = new Thread(new ThreadStart(Add));
    addThread.IsBackground = true;
    addThread.Start();
    }
    #region 显示时间操作
    /// <summary>
    /// 取得实时时间
    /// </summary>
    private void GetTime()
    {
    try
    {
    while (true)
    {
    string currentTime = string.Format("{0}.{1}", DateTime.Now.ToLongTimeString(), DateTime.Now.Millisecond);
    ShowTime(currentTime);
    Application.DoEvents();
    }
    }
    catch (Exception ex)
    {
    Console.WriteLine(ex.Message);
    }
    }
    // 定义显示时间操作委托,用于回调显示时间方法
    delegate void ShowTimeCallBack(string str);
    /// <summary>
    /// 实时显示时间
    /// </summary>
    /// <param name="str"></param>
    private void ShowTime(string str)
    {
    if (this.txtCurrentTime.InvokeRequired)
    {
    ShowTimeCallBack showTimeCallBack = new ShowTimeCallBack(ShowTime);
    this.Invoke(showTimeCallBack, new object[] { str });
    }
    else
    {
    this.txtCurrentTime.Text = str;
    }
    }
    #endregion
    #region 加法操作
    /// <summary>
    /// 加法操作
    /// </summary>
    private void Add()
    {
    try
    {
    while (true)
    {
    int i = Convert.ToInt32(this.txtOne.Text.Trim());
    int j = Convert.ToInt32(this.txtSecond.Text.Trim());
    ShowResult((i + j).ToString());
    Application.DoEvents();
    }
    }
    catch (Exception ex)
    {
    Console.WriteLine(ex.Message);
    }
    }
    // 定义加法操作委托,用于回调加法操作方法
    delegate void ShowResultCallBack(string result);
    /// <summary>
    /// 显示结果
    /// </summary>
    /// <param name="result"></param>
    private void ShowResult(string result)
    {
    if (this.txtResult.InvokeRequired)
    {
    // 写法1
    //ShowResultCallBack showResultCallBack = new ShowResultCallBack(ShowResult);
    //this.Invoke(showResultCallBack, new object[] { result });
    // 写法2
    //使用委托来赋值
    this.txtResult.Invoke(
    //委托方法
    new ShowResultCallBack(ShowResult),
    new object[] { result });
    }
    else
    {
    this.txtResult.Text = result;
    }
    }
    #endregion
    }
    }

    是不是很简单呢?
    出处参考:http://www.jb51.net/article/32052.htm

  • 相关阅读:
    20200910-1 每周例行报告
    刷题-力扣-209. 长度最小的子数组
    刷题-力扣-面试题 05.03. 翻转数位
    刷题-力扣-118. 杨辉三角
    刷题-力扣-1894. 找到需要补充粉笔的学生编号
    刷题-力扣-498. 对角线遍历
    刷题-力扣-45. 跳跃游戏 II
    刷题-力扣-55. 跳跃游戏
    刷题-力扣-1221. 分割平衡字符串
    刷题-力扣-654. 最大二叉树
  • 原文地址:https://www.cnblogs.com/mq0036/p/3678472.html
Copyright © 2011-2022 走看看