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