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