C#后台线程和UI的交互

转载 2015年11月18日 09:29:52

在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 );
        }
 
        UI设计#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)
        {
            bool bTime = false, bRandomNumbers = false;
 
            if( manageTime.Checked || manageBoth.Checked )
                bTime = true;
            if( manageNumbers.Checked || manageBoth.Checked )
                bRandomNumbers = true;
 
            if( bTime )
                currentTimeThread.Start();
             
            if( bRandomNumbers )
                randomNumbersThread.Start();
 
            start.Enabled = false;
            stop.Enabled = true;
            pause.Enabled = true;
        }
 
        private void stop_Click(object sender, System.EventArgs e)
        {
            bool bTime = false, bRandomNumbers = false;
 
            if( manageTime.Checked == true || manageBoth.Checked == true )
                bTime = true;
            if( manageNumbers.Checked == true || manageBoth.Checked == true )
                bRandomNumbers = true;
 
            if( bTime )
            {
                currentTimeThread.Abort();
                currentTimeThread.Join();
 
                currentTimeThread = new Thread(new ThreadStart(CountTime));
                currentTimeThread.IsBackground = true;
            }
 
            if( bRandomNumbers )
            {
                randomNumbersThread.Abort();
                randomNumbersThread.Join();
 
                randomNumbersThread = new Thread(new ThreadStart(GenerateRandomNumbers));
                randomNumbersThread.IsBackground = true;
            }
 
            start.Enabled = true;
            stop.Enabled = false;
            pause.Enabled = false;
        }
 
        private void pause_Click(object sender, System.EventArgs e)
        {
            bool bTime = false, bRandomNumbers = false;
 
            if( manageTime.Checked == true || manageBoth.Checked == true )
                bTime = true;
            if( manageNumbers.Checked == true || manageBoth.Checked == true )
                bRandomNumbers = true;
 
            if( bTime )
            {
                if( 0 != (currentTimeThread.ThreadState & ( ThreadState.Suspended | ThreadState.SuspendRequested ) ) )
                    currentTimeThread.Resume();
                else
                    currentTimeThread.Suspend();
            }
 
 
            if( bRandomNumbers )
            {
                if( 0 != (randomNumbersThread.ThreadState & ( ThreadState.Suspended | ThreadState.SuspendRequested ) ) )
                    randomNumbersThread.Resume();
                else
                    randomNumbersThread.Suspend();
            }
        }
 
        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);
            }
        }
 
       <span style="color: #ff0000;"><strong> 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);
</strong></span>}

     其实在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  的例子.

  

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

C#5.0 使用async轻松解决跨线程访问UI

在c/s程序中 如果界面元素(对应的事件

C# Winform 跨线程更新UI控件常用方法总结(转)

出处:http://www.tuicool.com/articles/FNzURb 概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建...

C# Winform 跨线程更新UI控件常用方法总结(转)

出处:http://www.tuicool.com/articles/FNzURb 概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建...
  • dragoo1
  • dragoo1
  • 2017年02月10日 12:35
  • 1789

Android的UI设计与后台线程交互

在Android的UI设计方面我们讲过“Android UI”设计官方教程。本文将讨论Android应用程序的线程模型以及如何使用线程来处理耗时较长的操作,而不是在主线程中执行,保证用户界面(UI)的...

Android的UI设计与后台线程交互

本文将讨论Android应用程序的线程模型以及如何使用线程来处理耗时较长的操作,而不是在主线程中执行,保证用户界面(UI)的流畅运行。本文还将阐述一些用户界面(UI)中与线程交互的API。 UI用户...

UI线程与后台线程交互设计5种方法

在一个Android 程序开始运行的时候,会单独启动一个Process。默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Co...
  • mark_qi
  • mark_qi
  • 2013年09月23日 12:29
  • 1841

UI线程与后台线程交互设计5种方法

在一个Android 程序开始运行的时候,会单独启动一个Process。默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Co...
  • GUITK
  • GUITK
  • 2014年03月17日 17:28
  • 1641

AsyncTask - 基本原理 后台线程和UI线程的交互

前面一个文章大概描述了一下任务是怎么被执行的?http://blog.csdn.net/zj510/article/details/51485120 其实也就是AsyncTask里面的doInBac...
  • zj510
  • zj510
  • 2016年05月24日 13:30
  • 574

UI线程与后台线程交互设计的5种方法

在android的设计思想中,为了确保用户顺滑的操作体 验。一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务。因此我们必须要重新开启一个后台线程运行这些任务。然而,往往这些任务最终又会直...
  • jys1115
  • jys1115
  • 2014年10月13日 21:33
  • 383
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C#后台线程和UI的交互
举报原因:
原因补充:

(最多只允许输入30个字)