C#多線程應用:子線程更新主窗體控件的值(二)

在上篇文章中,我已經給你們列了一個在主線程中實現的方式,這篇文章來給你們說說使用Invoke的方式的例子:html

對於不代理不太熟悉的朋友,建議先查查相關資料;web

例子一:安全

在C#中,直接在子線程中對窗體上的控件操做是會出現異常,這是因爲子線程和運行窗體的線程是不一樣的空間,所以想要在子線程來操做窗體上的控件,是不可能簡單的經過控件對象名來操做,但不是說不能進行操做,微軟提供了Invoke的方法,其做用就是讓子線程告訴窗體線程來完成相應的控件操做。函數

要實現該功能,有兩種方法能夠選擇:ui

 

一、在程序初始化的時候對要操做的控件設置下面的屬性:this

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls  =  false;spa

這樣,系統就不會再拋出上面所說的這個錯誤了。.net

從實質上說,該方法是經過採用取消線程安全保護模式的方式實現的,因此不建議採用。線程

 

二、基本思路以下:3d

把想對另外一線程中的控件實施的操做放到一個函數中,而後使用delegate代理那個函數,而且在那個函數中加入一個判斷,用InvokeRequired 來判斷調用這個函數的線程是否和控件線程處於同一線程中,若是是則直接執行對控件的操做,不然利用該控件的Invoke或BeginInvoke方法來執行這個代理。示例代碼以下:

複製代碼
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Windows.Forms;
  4 
  5 using System.Threading;
  6 
  7 namespace 子線程操做主線程窗體上的控件
  8 {
  9     public partial class frmMain : Form
 10     {
 11         /***************************************************** 定義該類的私有成員 ****************************************************/
 12         
 13         /// <summary>
 14         /// 定義一個隊列,用於記錄用戶建立的線程
 15         /// 以便在窗體關閉的時候關閉全部用於建立的線程
 16         /// </summary>
 17         private List<Thread> ChaosThreadList;
 18 
 19         /***************************************************** 該類的初始化相關函數 ****************************************************/
 20                 
 21         /// <summary>
 22         /// 窗體的初始化函數,初始化線程隊列ChaosThreadList
 23         /// </summary>
 24         public frmMain()
 25         {
 26             InitializeComponent();
 27             ChaosThreadList = new List<Thread>();
 28         }
 29                
 30         /// <summary>
 31         /// 窗體的關閉事件處理函數,在該事件中將以前建立的線程所有終止
 32         /// </summary>
 33         /// <param name="sender"></param>
 34         /// <param name="e"></param>
 35         private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
 36         {
 37             if (ChaosThreadList.Count > 0)
 38             {
 39                 //編列自定義隊列,將全部線程終止
 40                 foreach (Thread tWorkingThread in ChaosThreadList)
 41                 {
 42                     tWorkingThread.Abort();
 43                 }
 44             }
 45         }        
 46 
 47         /***************************************************** 定義該類的自定義函數 ****************************************************/
 48 
 49         /// <summary>
 50         /// 定義一個代理
 51         /// </summary>
 52         /// <param name="index"></param>
 53         /// <param name="MSG"></param>
 54         private delegate void DispMSGDelegate(int index,string MSG);
 55 
 56         /// <summary>
 57         /// 定義一個函數,用於向窗體上的ListView控件添加內容
 58         /// </summary>
 59         /// <param name="iIndex"></param>
 60         /// <param name="strMsg"></param>
 61         private void DispMsg(int iIndex,string strMsg)
 62         {
 63             if (this.lstMain.InvokeRequired==false)                      //若是調用該函數的線程和控件lstMain位於同一個線程內
 64             {
 65                 //直接將內容添加到窗體的控件上
 66                 ListViewItem lvi = new ListViewItem();
 67                 lvi.SubItems[0].Text = iIndex.ToString();
 68                 lvi.SubItems.Add(strMsg);
 69                 this.lstMain.Items.Insert(0, lvi);
 70             }
 71             else                                                        //若是調用該函數的線程和控件lstMain不在同一個線程
 72             {
 73                 //經過使用Invoke的方法,讓子線程告訴窗體線程來完成相應的控件操做
 74                 DispMSGDelegate DMSGD = new DispMSGDelegate(DispMsg);
 75 
 76                 //使用控件lstMain的Invoke方法執行DMSGD代理(其類型是DispMSGDelegate)
 77                 this.lstMain.Invoke(DMSGD, iIndex, strMsg);
 78                 
 79             }
 80         }
 81 
 82         /// <summary>
 83         /// 定義一個線程函數,用於循環向列表中添加數據
 84         /// </summary>
 85         private void Thread_DisplayMSG()
 86         {
 87             for (int i = 0; i < 10000; i++)
 88             {
 89                 DispMsg(i + 1, "Welcome you : " + (i + 1).ToString());
 90                 Thread.Sleep(10);
 91             }
 92         }
 93 
 94         /***************************************************** 定義該類的事件處理函數 ****************************************************/
 95 
 96         /// <summary>
 97         /// 【開始】按鈕的單擊事件處理函數,新建一個線程向窗體上的ListView控件填寫內容
 98         /// </summary>
 99         /// <param name="sender"></param>
100         /// <param name="e"></param>
101         private void btnBegin_Click(object sender, EventArgs e)
102         {
103             //建立一個新的線程
104             Thread tWorkingThread = new Thread(Thread_DisplayMSG);
105 
106             //將新建的線程加入到自定義線程隊列中,以便在窗體結束時關閉全部的線程
107             ChaosThreadList.Add(tWorkingThread);
108 
109             //開啓線程
110             tWorkingThread.Start();
111         }     
112 
113     }
114 }
複製代碼

 

 

這樣子就能夠實現用子線程去操做主線程窗體上的控件的內容,同時,又不影響主線程對窗體上其餘控件的響應。程序運行截圖以下:

 

 

 

 點擊[開始]按鈕,程序開啓一個新的線程,不斷向列表中添加新的數據,而同時不會影響主界面對其它控件(例如:文本框)的響應。

 

[P.S]:

INVOKE方法的做用:

它使該控件所在的線程執行Invoke方法參數中指定的代理,也就是使主線程執行咱們想對控件進行的操做。

出處:http://www.cnblogs.com/chaosimple/archive/2012/05/11/2495811.html

--------------------------------------------------------------------------------------------------------------------

例子二:

本文將詳細介紹C#利用子線程如何刷新主線程,須要瞭解更多的朋友能夠參考下
要求:以下圖,使用線程操做
一、實時顯示當前時間
二、輸入加數和被加數,自動出現結果

分析:兩個問題解決的方式相同,使用子線程進行時間操做和加法操做,而後刷新主線程的控件顯示結果
複製代碼 代碼以下:
using System;
using System.Threading;
using System.Windows.Forms;
namespace WinThread
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
/// <summary>
/// 初始化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void frmMain_Load(object sender, EventArgs e)
{
// 控件初始化
this.txtOne.Text = "0";
this.txtSecond.Text = "0";
// 顯示時間操做
Thread showTimeThread = new Thread(new ThreadStart(GetTime));
showTimeThread.IsBackground = true;
showTimeThread.Start();
// 加法操做
Thread addThread = new Thread(new ThreadStart(Add));
addThread.IsBackground = true;
addThread.Start();
}
#region 顯示時間操做
/// <summary>
/// 取得實時時間
/// </summary>
private void GetTime()
{
try
{
while (true)
{
string currentTime = string.Format("{0}.{1}", DateTime.Now.ToLongTimeString(), DateTime.Now.Millisecond);
ShowTime(currentTime);
Application.DoEvents();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
// 定義顯示時間操做委託,用於回調顯示時間方法
delegate void ShowTimeCallBack(string str);
/// <summary>
/// 實時顯示時間
/// </summary>
/// <param name="str"></param>
private void ShowTime(string str)
{
if (this.txtCurrentTime.InvokeRequired)
{
ShowTimeCallBack showTimeCallBack = new ShowTimeCallBack(ShowTime);
this.Invoke(showTimeCallBack, new object[] { str });
}
else
{
this.txtCurrentTime.Text = str;
}
}
#endregion
#region 加法操做
/// <summary>
/// 加法操做
/// </summary>
private void Add()
{
try
{
while (true)
{
int i = Convert.ToInt32(this.txtOne.Text.Trim());
int j = Convert.ToInt32(this.txtSecond.Text.Trim());
ShowResult((i + j).ToString());
Application.DoEvents();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
// 定義加法操做委託,用於回調加法操做方法
delegate void ShowResultCallBack(string result);
/// <summary>
/// 顯示結果
/// </summary>
/// <param name="result"></param>
private void ShowResult(string result)
{
if (this.txtResult.InvokeRequired)
{
// 寫法1
//ShowResultCallBack showResultCallBack = new ShowResultCallBack(ShowResult);
//this.Invoke(showResultCallBack, new object[] { result });
// 寫法2
//使用委託來賦值
this.txtResult.Invoke(
//委託方法
new ShowResultCallBack(ShowResult),
new object[] { result });
}
else
{
this.txtResult.Text = result;
}
}
#endregion
}
}

是否是很簡單呢?
出處參考:http://www.jb51.net/article/32052.htm

相關文章
相關標籤/搜索