在C#中,从Main()方法开始一个默认的线程,一般称之为主线程,如果在这个进行一些非常耗CPU的计算,那么UI界面就会被挂起而处于假死状态,也就是说无法和用户进行交互了,特别是要用类似进度条来实时显示一些提示信息的时候,这种情况就显得很糟糕。如果多开一些线程来完成一些耗时的计算,那么工作线程也是无法如此更新UI界面中的元素的,比如直接显示一个提示信息:label1.Text=outstring,原因很简单UI属于默认的主线程,而线程间是不能这样直接访问彼此的成员的。
  如果要解决以上的两个问题,那么可以借助C#中的Delegate和控件类中的Invoke()方法来搞定。
  这里给出的例子比较简单,主要思路是:在Main()中启动其它的线程作为后台进程,其中一个线程是实时显示当前的时间,一个线程是显示一些随机数,这样一来三个线程同时运行,彼此通过代理来联系。


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;

namespace MutliThreadedWinFormsApp
{
    public class Form1 : System.Windows.Forms.Form
    {
        private Thread currentTimeThread = null;
        private Thread randomNumbersThread = null;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.TextBox currentTime;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox randomNumbers;
        private System.Windows.Forms.GroupBox threadManager;
        private System.Windows.Forms.Button pause;
        private System.Windows.Forms.Button stop;
        private System.Windows.Forms.Button start;
        private System.Windows.Forms.RadioButton manageTime;
        private System.Windows.Forms.RadioButton manageNumbers;
        private System.Windows.Forms.RadioButton manageBoth;
        private System.ComponentModel.Container components = null;

        public Form1()
        {
            InitializeComponent();
            
            //创建新的工作线程
            currentTimeThread = new Thread(new ThreadStart(CountTime));
            currentTimeThread.IsBackground = true;
            
            randomNumbersThread = new Thread(new ThreadStart(GenerateRandomNumbers));
            randomNumbersThread.IsBackground = true;
        }

        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null) 
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region UI设计
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.currentTime = new System.Windows.Forms.TextBox();
            this.label2 = new System.Windows.Forms.Label();
            this.randomNumbers = new System.Windows.Forms.TextBox();
            this.threadManager = new System.Windows.Forms.GroupBox();
            this.manageBoth = new System.Windows.Forms.RadioButton();
            this.manageNumbers = new System.Windows.Forms.RadioButton();
            this.manageTime = new System.Windows.Forms.RadioButton();
            this.pause = new System.Windows.Forms.Button();
            this.stop = new System.Windows.Forms.Button();
            this.start = new System.Windows.Forms.Button();
            this.threadManager.SuspendLayout();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(8, 24);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(79, 13);
            this.label1.TabIndex = 0;
            this.label1.Text = "现在的时间:";
            // 
            // currentTime
            // 
            this.currentTime.Location = new System.Drawing.Point(88, 23);
            this.currentTime.Name = "currentTime";
            this.currentTime.ReadOnly = true;
            this.currentTime.Size = new System.Drawing.Size(157, 20);
            this.currentTime.TabIndex = 1;
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(14, 56);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(43, 13);
            this.label2.TabIndex = 2;
            this.label2.Text = "随机数";
            // 
            // randomNumbers
            // 
            this.randomNumbers.Location = new System.Drawing.Point(16, 72);
            this.randomNumbers.Name = "randomNumbers";
            this.randomNumbers.ReadOnly = true;
            this.randomNumbers.Size = new System.Drawing.Size(229, 20);
            this.randomNumbers.TabIndex = 3;
            // 
            // threadManager
            // 
            this.threadManager.Controls.Add(this.manageBoth);
            this.threadManager.Controls.Add(this.manageNumbers);
            this.threadManager.Controls.Add(this.manageTime);
            this.threadManager.Controls.Add(this.pause);
            this.threadManager.Controls.Add(this.stop);
            this.threadManager.Controls.Add(this.start);
            this.threadManager.Location = new System.Drawing.Point(16, 104);
            this.threadManager.Name = "threadManager";
            this.threadManager.Size = new System.Drawing.Size(229, 154);
            this.threadManager.TabIndex = 7;
            this.threadManager.TabStop = false;
            this.threadManager.Text = "工作线程控制";
            // 
            // manageBoth
            // 
            this.manageBoth.Location = new System.Drawing.Point(34, 74);
            this.manageBoth.Name = "manageBoth";
            this.manageBoth.Size = new System.Drawing.Size(104, 16);
            this.manageBoth.TabIndex = 12;
            this.manageBoth.Text = "同时运行";
            this.manageBoth.CheckedChanged += new System.EventHandler(this.manageBoth_CheckedChanged);
            // 
            // manageNumbers
            // 
            this.manageNumbers.Location = new System.Drawing.Point(34, 50);
            this.manageNumbers.Name = "manageNumbers";
            this.manageNumbers.Size = new System.Drawing.Size(104, 16);
            this.manageNumbers.TabIndex = 11;
            this.manageNumbers.Text = "更新随机数线程";
            this.manageNumbers.CheckedChanged += new System.EventHandler(this.manageNumbers_CheckedChanged);
            // 
            // manageTime
            // 
            this.manageTime.Location = new System.Drawing.Point(34, 26);
            this.manageTime.Name = "manageTime";
            this.manageTime.Size = new System.Drawing.Size(104, 16);
            this.manageTime.TabIndex = 10;
            this.manageTime.Text = "更新时间线程";
            this.manageTime.CheckedChanged += new System.EventHandler(this.manageTime_CheckedChanged);
            // 
            // pause
            // 
            this.pause.Location = new System.Drawing.Point(84, 115);
            this.pause.Name = "pause";
            this.pause.Size = new System.Drawing.Size(54, 23);
            this.pause.TabIndex = 9;
            this.pause.Text = "暂停";
            this.pause.Click += new System.EventHandler(this.pause_Click);
            // 
            // stop
            // 
            this.stop.Location = new System.Drawing.Point(158, 115);
            this.stop.Name = "stop";
            this.stop.Size = new System.Drawing.Size(51, 23);
            this.stop.TabIndex = 8;
            this.stop.Text = "停止";
            this.stop.Click += new System.EventHandler(this.stop_Click);
            // 
            // start
            // 
            this.start.Location = new System.Drawing.Point(6, 115);
            this.start.Name = "start";
            this.start.Size = new System.Drawing.Size(50, 23);
            this.start.TabIndex = 7;
            this.start.Text = "开始";
            this.start.Click += new System.EventHandler(this.start_Click);
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(253, 271);
            this.Controls.Add(this.threadManager);
            this.Controls.Add(this.randomNumbers);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.currentTime);
            this.Controls.Add(this.label1);
            this.Name = "Form1";
            this.Text = "工作线程和UI的交互";
            this.Closing += new System.ComponentModel.CancelEventHandler(this.Form1_Closing);
            this.threadManager.ResumeLayout(false);
            this.ResumeLayout(false);
            this.PerformLayout();
        }
        #endregion

        
        [STAThread]
        static void Main() 
        {
            Application.Run(new Form1());
        }

        private void start_Click(object sender, System.EventArgs e)
        

        private void stop_Click(object sender, System.EventArgs e)
        

        private void pause_Click(object sender, System.EventArgs e)
        

        private void manageTime_CheckedChanged(object sender, System.EventArgs e)
        {
        }

        private void manageNumbers_CheckedChanged(object sender, System.EventArgs e)
        {
        }

        private void manageBoth_CheckedChanged(object sender, System.EventArgs e)
        {
        }

        /// <summary>
        /// 注意其Invoke的使用,其有两种使用形式
        /// public void Invoke(System.Delegate delegate);
        /// public void Invoke(System.Delegate delegate, object [] args);
        /// </summary>
        public void CountTime()
        {
            while(true)
            {
                Invoke(new UpdateTimeDelegate(updateCurrentTime));
                Thread.Sleep(1000);
            }
        }

        public void GenerateRandomNumbers()
        {
            int [] randomNumbers = new int[10];
            Random r = new Random();

            while(true)
            {
                for(int i = 0; i < randomNumbers.Length; i++)
                    randomNumbers[i] = r.Next(1, 100);

                Invoke(new UpdateRandomNumbers(updateRandomNumbers), new object[] { randomNumbers });
                Thread.Sleep(500);
            }
        }

        /// <summary>
        /// 负责更新UI界面中时间显示的函数
        /// </summary>
        private void updateCurrentTime()
        {
            currentTime.Text = DateTime.Now.ToLongTimeString();
        }

        
        /// <summary>
        /// 负责更新UI界面中随机数显示的函数
        /// </summary>
        /// <param name="numbers"></param>
        private void updateRandomNumbers(int [] numbers)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            foreach(int i in numbers)
                sb.Append(i.ToString()).Append(", ");

            randomNumbers.Text = sb.ToString();
        }

        private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if( (randomNumbersThread.ThreadState & ThreadState.Running) == ThreadState.Running )
                randomNumbersThread.Abort();

            randomNumbersThread = null;
            
            if( (currentTimeThread.ThreadState & ThreadState.Running) == ThreadState.Running )
                currentTimeThread.Abort();

            currentTimeThread = null;
        }
    }

    public delegate void UpdateTimeDelegate();
    public delegate void UpdateRandomNumbers(int [] numbers);
}

其实在C# 2.0 中所有的Control类都有Invoke()方法,如果负责更新UI元素的函数不是定义在Main()中,那么必须首先检测Control类中的InvokeRequired属性。举个例子吧,注意setProgressBarValue()函数中调用自己的方式.
        //在工作线程中更新主窗口进度条
        public void setProgressBarValue(ProgressBar progressBar1,int value)
        {
            if (progressBar1.InvokeRequired)
            {
                object[] parameters = new object[] { value };
                progressBar1.Invoke(new setProgressBarValueDelegate(setProgressBarValue), parameters);
            }
            else
                progressBar1.Value = value;
        }
  这里的一些代码参考了http://www.codeproject.com  的例子.

C#中后台线程和UI线程的交互的更多相关文章

  1. 新建线程与UI线程间的通信

    现在用一个实例来演示一下自己的新建线程与UI线程间的通信. UI界面包含3个控件: 一个输入框,用来输入数字: 一个显示框,用来显示从2开始,到输入数字之间的所有质数: 一个按钮,点击后获取输入框输入 ...

  2. [Android学习笔记]子线程更新UI线程方法之Handler

    关于此笔记 不讨论: 1.不讨论Handler实现细节 2.不讨论android线程派发细节 讨论: 子线程如何简单的使用Handler更新UI 问题: android开发时,如何在子线程更新UI? ...

  3. OkHttp3几个简单的例子和在子线程更新UI线程的方法

    okHttp用于android的http请求.据说很厉害,我们来一起尝尝鲜.但是使用okHttp也会有一些小坑,后面会讲到如何掉进坑里并爬出来. 首先需要了解一点,这里说的UI线程和主线程是一回事儿. ...

  4. C# 委托 / 跨线程访问UI / 线程间操作无效: 从不是创建控件“Form1”的线程访问它

    C# 委托 / 跨线程访问UI /  线程间操作无效: 从不是创建控件“Form1”的线程访问它 网上的代码都比较复杂,还是这个简单 见代码, 简易解决办法: 主窗体代码 using System; ...

  5. Android ActivityThread(主线程或UI线程)简介

    1. ActivityThread功能 它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client ...

  6. WPF线程获取UI线程

    WPF中只能是UI线程才可以改变UI控件相关,当采用多线程工作时,可用以下代码获取 UI线程进行操作: App.Current.Dispatcher.Invoke((Action)delegate() ...

  7. C#用副线程改主线程(UI线程)的控件属性的方法(包括Winform和WPF)

    C#用副线程去试图修改主线程的UI控件会报出异常,解决方案是使用副线程注册事件通知主线程自己去修改UI控件 在winform中,方法如下 private void button1_Click(obje ...

  8. 主线程与UI线程简介

    ---------------siwuxie095                             Java 程序的主线程     当 Java 程序启动时,一个线程立刻运行,该线程通常叫做程 ...

  9. C#在非UI线程调用UI线程的控件

    首先需要定义一个委托(delegate): private delegate void delegateSetProcessBarVal(int value); 然后定义一个方法来执行具体的操作: p ...

随机推荐

  1. Java屌炸天学习路线图

            第一阶段:Java基础篇 编号 课程 课程目录 打包下载地址 讲师 01 J2SE(40课时) http://www.java1234.com/zy001.html http://pa ...

  2. 【微信】根据appid, secret, code获取用户基本信息

    function getUserInfo(){ $appid = "yourappid"; $secret = "yoursecret"; $code = $_ ...

  3. HDU 6108.小C的倍数问题 (2017"百度之星"程序设计大赛 - 初赛(A)1001)

    补完题?不存在的. 这么久了,还是一条咸鱼,看一堆乱七八糟的东西,写一堆没用的水题,一点进步都没有,还是那么菜,菜的掉渣. 这个百毒之星初赛A还会写两道最简单的水题,初赛B一点也不会,菜的难过... ...

  4. 第十四届华中科技大学程序设计竞赛 C Professional Manager【并查集删除/虚点】

    题目描述 It's universally acknowledged that there're innumerable trees in the campus of HUST. Thus a pro ...

  5. 51nod 1873 初中的算术【Java BigDecimal/高精度小数】

    1873 初中的算术 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题  收藏  关注 Noder现在上初三了,正在开始复习中考.他每天要计算型如 (a× a× a× ...

  6. springboot 2.0+ 自定义拦截器

    之前项目的springboot自定义拦截器使用的是继承WebMvcConfigurerAdapter重写常用方法的方式来实现的. 以下WebMvcConfigurerAdapter 比较常用的重写接口 ...

  7. 解决虚拟机安装tomcat主机访问不到

    在wmware中安装linux后安装好数据库,JDK及tomcat后启动服务,虚拟机中可以访问,但是主机却无法访问,但是同时主机和虚拟机之间可以ping的通.解决方法是关闭虚拟机中的防火墙服务.桌面- ...

  8. Mycat查询时出现:Error Code: 1064. can't find any valid datanode

    说明:这个错误是查询时条件字段超过了范围导致的. 解释: 为什么会出现范围问题? 一般在MySQL查询时不会因为字段的值超过了范围而导致的,但是在Mycat中却不是这样认为的,Mycat是由于采用的分 ...

  9. 内存或磁盘空间不足,Microsoft Office Excel 无法再次打开或保存任何文档。 [问题点数:20分,结帖人wenyang2004]

    在环境里是没有问题的 就是发布网站后,会出现“/”应用程序中的服务器错误.--------------------------------------------------------------- ...

  10. sql server 2005 链接服务器:未将服务器 配置为用于 RPC

    原文:sql server 2005 链接服务器:未将服务器 配置为用于 RPC  RPC: 远程过程调用(Remote Procedure Calls) --LinkedServer.Rpc 属性: ...