C#后台线程和UI的交互
http://www.cnblogs.com/Wizardh/articles/963097.html
在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设计
[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);
}
}
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 的例子.


浙公网安备 33010602011771号