前言
在很多情況下,我們都會使用到多線程,在使用多線程的時候,我們很多時候又會去訪問控件,這里面就會出現很多問題!!!我以一個最常見的,我們經常會用到的例子來講講,在提高自己水平的同時,希望能給大家帶來一些方便,有不對的地方還請留言告知,以便及時更正自己的錯誤思想,先謝謝啦!
1、多線程使用
有時候在執行一段程序時會耗時比較長,這時候會發現,我們的窗體就拖不動了,就像卡死了,但那段耗時程序執行完之后,就恢復OK了,這就是我們常說的程序假死!代碼如下:

private void btnCalculate_Click(object sender, EventArgs e) { Calculate(); } private void Calculate() { for (int i = 0; i < 500; i++) { Thread.Sleep(5); } }
這時候使用多線程就很容易解決程序假死問題,修改代碼如下:

private void btnCalculate_Click(object sender, EventArgs e) { Thread mythread = new Thread(Calculate); mythread.IsBackground = true; //設置為後臺線程,程式關閉后進程也關閉,如果不設置true,則程式關閉,此線程還在內存,不會關閉 mythread.Start(); } private void Calculate() { Stopwatch stopwatch = Stopwatch.StartNew(); for (int i = 0; i < 500; i++) { Thread.Sleep(5); } stopwatch.Stop(); long lSearchTime = stopwatch.ElapsedMilliseconds; MessageBox.Show(lSearchTime.ToString()+"毫秒"); }
2、訪問控件方式
以上我們就解決了程序假死的問題,可需求又來了,我想知道計算的進度,能直觀的感受到計算多少了,是不是快計算完了,這時候我們想到了進度條,這時候我們就加入進度條唄,代碼如下:

private void btnCalculate_Click(object sender, EventArgs e) { Thread mythread = new Thread(Calculate); mythread.IsBackground = true; //設置為後臺線程,程式關閉后進程也關閉,如果不設置true,則程式關閉,此線程還在內存,不會關閉 mythread.Start(); } private void Calculate() { Stopwatch stopwatch = Stopwatch.StartNew(); progressBarWay.Maximum = 500; for (int i = 0; i < 500; i++) { Thread.Sleep(5); progressBarWay.Value = i; } stopwatch.Stop(); long lSearchTime = stopwatch.ElapsedMilliseconds; MessageBox.Show(lSearchTime.ToString()+"毫秒"); }
以上代碼運行的話程序報錯:報錯信息為:從不是創建控件控件名稱 的線程訪問它
下面有兩種方式來實現訪問控件:
1、不安全方式訪問控件
訪問 Windows 窗體控件本質上不是線程安全的。 如果有兩個或多個線程操作某一控件的狀態,則可能會迫使該控件進入一種不一致的狀態。 還可能會出現其他與線程相關的 Bug,例如爭用情況和死鎖。 確保以線程安全方式訪問控件非常重要 ----MSDN
ProgressBar.CheckForIllegalCrossThreadCalls = false; 只需要加入此段代碼就OK,不過不推薦使用
2、安全方式訪問控件
下面介紹一下我通過安全方式訪問ProgressBar控件實現的方法
總結為四步:
定義實現安全訪問控件的委託,第一步:定義帶參數委託Mydelegate,第二步:定義委託要實現的方法SetprogressBar,與委託聲明一致,第三步:聲明並實例化委託並傳入委託要實現的方法,第四步:在實際使用地方調用this.Invoke(mydele, mybar, SelectStatus.myVisible)參數1:聲明的委託名,參數2:委託變量的第一個參數:參數2:委託聲明的第二個參數,具體的代碼如下:

public partial class SafetyWay : Form { Mydelegate mydele = null; ProgressBar mybar = new ProgressBar(); public SafetyWay() { InitializeComponent(); mydele = new Mydelegate(SetprogressBar); //第三步:聲明並實例化委託並傳入委託要實現的方法 mydele= new Mydelegate(SetprogressBar) } private void btnCalculate_Click(object sender, EventArgs e) { Thread mythread = new Thread(Calculate); mythread.IsBackground = true; //設置為後臺線程,程式關閉后進程也關閉,如果不設置true,則程式關閉,此線程還在內存,不會關閉 mythread.Start(); } private void Calculate() { mybar.Visible = true; this.Invoke(mydele, mybar, SelectStatus.myVisible); Stopwatch stopwatch = Stopwatch.StartNew(); mybar.Maximum = 500; this.Invoke(mydele, mybar, SelectStatus.myMaximum); //第四步:在實際使用地方調用this.Invoke(mydele, mybar, SelectStatus.myVisible)參數1:聲明的委託名,參數2:委託變量的第一個參數:參數2:委託聲明的第二個參數 for (int i = 0; i < 500; i++) { Thread.Sleep(5); mybar.Value = i; this.Invoke(mydele, mybar, SelectStatus.myValue); } mybar.Visible = false; this.Invoke(mydele, mybar, SelectStatus.myVisible); stopwatch.Stop(); long lSearchTime = stopwatch.ElapsedMilliseconds; MessageBox.Show(lSearchTime.ToString() + "毫秒"); } #region 通過委託要實現的方法,設置ProgressBar的屬性值 private void SetprogressBar(ProgressBar myBar, SelectStatus status) //第二步:定義委託要實現的方法SetprogressBar,與委託聲明一致 { if (status == SelectStatus.myVisible) { progressBarWay.Visible = myBar.Visible; } else if (status == SelectStatus.myValue) { progressBarWay.Value = myBar.Value; } else if (status == SelectStatus.myMaximum) { progressBarWay.Maximum = myBar.Maximum; } } #endregion } #region 定義枚舉,記錄要改變的屬性,實際意義如name所示 public enum SelectStatus { myVisible, myValue, myMaximum } #endregion public delegate void Mydelegate(ProgressBar myBar, SelectStatus status); //定義實現安全訪問控件的委託,第一步:定義帶參數委託Mydelegate
以上的代碼實現方式相對繁瑣,下面給出使用lambda表達式的方式來實現會發現代碼非常簡單,代碼如下:
private void btnCalculate_Click(object sender, EventArgs e) { Thread mythread = new Thread(Calculate); mythread.IsBackground = true; //設置為後臺線程,程式關閉后進程也關閉,如果不設置true,則程式關閉,此線程還在內存,不會關閉 mythread.Start(); } private void Calculate() { this.Invoke(new Action(() => { progressBarWay.Visible = true; })); Stopwatch stopwatch = Stopwatch.StartNew(); this.Invoke(new Action(() => { progressBarWay.Maximum = 500; })); for (int i = 0; i < 500; i++) { Thread.Sleep(5); this.Invoke(new Action(() => { progressBarWay.Value = i; })); } this.Invoke(new Action(() => { progressBarWay.Visible = false; })); stopwatch.Stop(); long lSearchTime = stopwatch.ElapsedMilliseconds; MessageBox.Show(lSearchTime.ToString() + "毫秒"); }
上面的代碼樣子好像變化了不少,其實在編譯后編譯器會為我們上面省略的一系列代碼再加上去的。
結束語
以上就是我實際使用中總結的方法,希望對大家有所幫助,附上demo下載地址:http://files.cnblogs.com/beimeng/demo.rar
最后再附上一個獲取ArrayList 數組最大值最小值的方法:

ArrayList alist = new ArrayList(); alist.Add(5); alist.Add(9); alist.Add(10); alist.Add(3); alist.Add(6); alist.Add(3); int[] myArray = (int[])alist.ToArray(typeof(int)); //將ArrayList轉換為固定類型的Array的方法 Array.Sort(myArray); //將Array從小到大進行排序 int Max = myArray[myArray.Length - 1]; int Min = myArray[0];