C#中后台线程和UI线程的交互
在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线程的交互的更多相关文章
- 新建线程与UI线程间的通信
现在用一个实例来演示一下自己的新建线程与UI线程间的通信. UI界面包含3个控件: 一个输入框,用来输入数字: 一个显示框,用来显示从2开始,到输入数字之间的所有质数: 一个按钮,点击后获取输入框输入 ...
- [Android学习笔记]子线程更新UI线程方法之Handler
关于此笔记 不讨论: 1.不讨论Handler实现细节 2.不讨论android线程派发细节 讨论: 子线程如何简单的使用Handler更新UI 问题: android开发时,如何在子线程更新UI? ...
- OkHttp3几个简单的例子和在子线程更新UI线程的方法
okHttp用于android的http请求.据说很厉害,我们来一起尝尝鲜.但是使用okHttp也会有一些小坑,后面会讲到如何掉进坑里并爬出来. 首先需要了解一点,这里说的UI线程和主线程是一回事儿. ...
- C# 委托 / 跨线程访问UI / 线程间操作无效: 从不是创建控件“Form1”的线程访问它
C# 委托 / 跨线程访问UI / 线程间操作无效: 从不是创建控件“Form1”的线程访问它 网上的代码都比较复杂,还是这个简单 见代码, 简易解决办法: 主窗体代码 using System; ...
- Android ActivityThread(主线程或UI线程)简介
1. ActivityThread功能 它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client ...
- WPF线程获取UI线程
WPF中只能是UI线程才可以改变UI控件相关,当采用多线程工作时,可用以下代码获取 UI线程进行操作: App.Current.Dispatcher.Invoke((Action)delegate() ...
- C#用副线程改主线程(UI线程)的控件属性的方法(包括Winform和WPF)
C#用副线程去试图修改主线程的UI控件会报出异常,解决方案是使用副线程注册事件通知主线程自己去修改UI控件 在winform中,方法如下 private void button1_Click(obje ...
- 主线程与UI线程简介
---------------siwuxie095 Java 程序的主线程 当 Java 程序启动时,一个线程立刻运行,该线程通常叫做程 ...
- C#在非UI线程调用UI线程的控件
首先需要定义一个委托(delegate): private delegate void delegateSetProcessBarVal(int value); 然后定义一个方法来执行具体的操作: p ...
随机推荐
- 优化html中mp4视频加载速度
如果使用参数faststart就会在生成完上边结构之后将moov移动到mdat前面:ffmpeg –i input.flv –c copy –f mp4 –movflags faststart out ...
- Jekyll搭建个人博客
网上也有HEXO 搭建的博客,有人说使用 HEXO 在多台电脑上发布博客,操作起来并不是那么方便,所以使用Jekyll 来搭建. Jekyll配置 1,安装ruby环境 Windows系统使用Ruby ...
- java数组集合
一.ArrayList 1. ArrayList底层采用数组实现,当使用不带参数的构造方法生成ArrayList对象时,实际上会在底层生成一个长度为10的Object类型数组2. 如果增加的元素个数超 ...
- 信息批量提取工具bulk-extractor
信息批量提取工具bulk-extractor 在数字取证中,通常需要面对海量的数据,如几百GB甚至TB级别的数据.从这些海量数据中,提取有价值的数据是一个漫长.枯燥.繁琐的过程.Kali Linu ...
- JavaEE目录
第一章: Spring介绍 Spring项目搭建 Spring概念 第二章: Sprin配置详解 属性注入(构造方法注入,设值注入) 实例化(构造器(空参构造器),静态工厂,工厂方法) 装配(xml方 ...
- 解决Eclipse 变量名的自动补全问题
大家使用eclipse敲代码的时候,是不是都被这样一个问题困扰着.就是键入一个变量名的时候,会自动提示补全:在你的变量名后面加上类型的名字!这个时候,你就必须键入Esc才不会自动补全你的变量,如果你键 ...
- ARM常用汇编指令介绍
b 跳转指令(跳转范围为32Mb) bl 带返回地址的跳转,指令自动将下一条指令的地址复制到R14寄存器,然后跳转到指定地址去执行,执行完后返回到下一条指令处执行 pc 寄存器R1 ...
- .Net中的不可变集合(Immutable Collection)简介
今天发现MS在Nuget上发布了一个Immutable Collection的程序集,提供了对不可变对象的集合的支持. 简单的看了一下,貌似支持的还比较全: ImmutableArray<T&g ...
- 细说JavaScript对象(4): for in 循环
如同 in 运算符一样,使用 for in 循环遍历对象属性时,也将往上遍历整个原型链. // Poisoning Object.prototype Object.prototype.bar = 1; ...
- [sharepoint]文档库,文件夹授权
写在前面 在项目中用到了文档库授权的方法,这里将查询到的方式总结一下. 涉及到的方法 在逻辑中用到的方法. /// <summary> /// 获取sharepoint站点角色定义 res ...