線程池學習

 

WinForm開發中,咱們一般不但願當窗體上點了某個按鈕執行某個業務的時候,窗體就被卡死了,直到該業務執行完畢後才緩過來。一個最直接的方法即是使用多線程。多線程編程的方式在WinForm開發中必不可少。
本文介紹在WinForm開發中如何使用多線程,以及在線程中如何經過Control.Invoke方法返回窗體主線程執行相關操做。

語法:
  1. public Object Invoke(
  2.         Delegate method
  3. )
複製代碼
參數
method類型:System.Delegate
包含要在控件的線程上下文中調用的方法的委託。

返回值
類型:System.Object
正在被調用的委託的返回值,或者若是委託沒有返回值,則爲 null。

備註
--------------------------------------------------------------------------------
委託相似於 C 或 C++ 語言中的函數指針。委託將對方法的引用封裝在委託對象中。而後能夠將委託對象傳遞給調用所引用的方法的代碼,隨後要在編譯時調用的方法能夠是未知的。與 C 或 C++ 中的函數指針不一樣的是,委託是面向對象的、類型安全的和更保險的。

若是當前控件的基礎窗口句柄尚不存在,則 Invoke 方法沿控件的父級鏈搜索,直到找到具備窗口句柄的控件或窗體爲止。若是找不到合適的句柄,Invoke 方法將引起異常。在調用過程當中引起的異常將傳播回調用方。

注意

若是已經建立控件的句柄,則除了 InvokeRequired 屬性之外,控件上還有四個能夠從任何線程上安全調用的方法,它們是:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。在後臺線程上建立控件的句柄以前調用 CreateGraphics 可能會致使非法的跨線程調用。對於全部其餘方法調用,則應使用調用 (invoke) 方法之一封送對控件的線程的調用。


委託能夠是 EventHandler 的實例,在此狀況下,發送方參數將包含此控件,而事件參數將包含 EventArgs.Empty。委託還能夠是 MethodInvoker 的實例或採用 void 參數列表的其餘任何委託。調用 EventHandler 或 MethodInvoker 委託比調用其餘類型的委託速度更快。

注意
若是應當處理消息的線程再也不處於活動狀態,則可能會引起異常。

如下爲MSDN示例:
  1.  
  2. using System;
  3. using System.Drawing;
  4. using System.Windows.Forms;
  5. using System.Threading;
  6.  
  7.    public class MyFormControl : Form
  8.    {
  9.       public delegate void AddListItem();
  10.       public AddListItem myDelegate;
  11.       private Button myButton;
  12.       private Thread myThread;
  13.       private ListBox myListBox;
  14.       public MyFormControl()
  15.       {
  16.          myButton = new Button();
  17.          myListBox = new ListBox();
  18.          myButton.Location = new Point(72, 160);
  19.          myButton.Size = new Size(152, 32);
  20.          myButton.TabIndex = 1;
  21.          myButton.Text = "Add items in list box";
  22.          myButton.Click += new EventHandler(Button_Click);
  23.          myListBox.Location = new Point(48, 32);
  24.          myListBox.Name = "myListBox";
  25.          myListBox.Size = new Size(200, 95);
  26.          myListBox.TabIndex = 2;
  27.          ClientSize = new Size(292, 273);
  28.          Controls.AddRange(new Control[] {myListBox,myButton});
  29.          Text = " 'Control_Invoke' example";
  30.          myDelegate = new AddListItem(AddListItemMethod);
  31.       }
  32.       static void Main()
  33.       {
  34.          MyFormControl myForm = new MyFormControl();
  35.          myForm.ShowDialog();
  36.       }
  37.       public void AddListItemMethod()
  38.       {
  39.          String myItem;
  40.          for(int i=1;i<6;i++)
  41.          {
  42.             myItem = "MyListItem" + i.ToString();
  43.             myListBox.Items.Add(myItem);
  44.             myListBox.Update();
  45.             Thread.Sleep(300);
  46.          }
  47.       }
  48.       private void Button_Click(object sender, EventArgs e)
  49.       {
  50.          myThread = new Thread(new ThreadStart(ThreadFunction));
  51.          myThread.Start();
  52.       }
  53.       private void ThreadFunction()
  54.       {
  55.          MyThreadClass myThreadClassObject  = new MyThreadClass(this);
  56.          myThreadClassObject.Run();
  57.       }
  58.    }
  59.  
  60. // The following code assumes a 'ListBox' and a 'Button' control are added to a form,
  61. // containing a delegate which encapsulates a method that adds items to the listbox.
  62.  
  63.    public class MyThreadClass
  64.    {
  65.       MyFormControl myFormControl1;
  66.       public MyThreadClass(MyFormControl myForm)
  67.       {
  68.          myFormControl1 = myForm;
  69.       }
  70.  
  71.       public void Run()
  72.       {
  73.          // Execute the specified delegate on the thread that owns
  74.          // 'myFormControl1' control's underlying window handle.
  75.          myFormControl1.Invoke(myFormControl1.myDelegate);
  76.       }
  77.    }
  78.  
複製代碼
關於線程個數問題:一個線程默認分配堆棧是 1m
一個進程最大佔 2G;因此致使一個進程內最大線程是2024個理論線程。
固然確定不是了。若是編譯的時候,能夠設置每一個線程的最大堆棧的佔用。
多線程編程:一個是本身開一個線程。一個是把任務交給線程池。交給線程池的就是要求交給的是委託;定義一個委託的實質是:定義一種自定義的消息類型,
這樣這個消息類型定義後,能夠註冊與這個消息類型對應得處理過程,註冊方法是:實例化委託或者給定義事件的實例化對象綁定想應的處理過程;當事件發生時會把消息傳給這個過程;也就是指針指向這個實例化時候或者綁定時候的處理過程;從而實現委託的目的。在使用委託的時候,注意事項是線程間的安全問題,也就是若是委託要操做的是控件的屬性,那就須要經過控件自己經過調用這個委託,調用方式是有規定的,用的是: invoke()從而避免了生成控件的線程和操做控件的線程之間的衝突。由於 Control.Invoke含義是將方法委託給擁有該Control的線程去執行
. WinForm多線程編程
1. new Thread()
    新開一個線程,執行一個方法,沒有參數傳遞:
  1. private void DoWork() {
  2.             Thread t = new Thread(new ThreadStart(this.DoSomething));
  3.             t.Start();
  4.         }
  5.         private void DoSomething() {
  6.             MessageBox.Show("thread start");
  7.         }
複製代碼
新開一個線程,執行一個方法,並傳遞參數:
  1. private void DoWork() {
  2.             Thread t = new Thread(new ParameterizedThreadStart(this.DoSomething));
  3.             t.Start("guozhijian");
  4.         }
  5.         private void DoSomething(object o) {
  6.             MessageBox.Show(o.ToString());
  7.         }
複製代碼
參數定義爲 object類型。
2. ThreadPool
    衆所周知,新開一個線程代價是很高昂的,若是咱們每一個操做都新開一個線程,那麼太浪費了,因而,下面使用線程池。
    無參數傳遞:
  1. private void DoWork() {
  2.             ThreadPool.QueueUserWorkItem(new WaitCallback(this.DoSomething));
  3.         }
  4.         private void DoSomething(object o) {
  5.             MessageBox.Show("thread start");
  6.         }
複製代碼
有參數傳遞:
  1. private void DoWork() {
  2.             ThreadPool.QueueUserWorkItem(new WaitCallback(this.DoSomething), "guozhijian");
  3.         }
  4.         private void DoSomething(object o) {
  5.             MessageBox.Show(o.ToString());
  6.         }
複製代碼
使用匿名方法更靈活:
  1. private void DoWork() {
  2.             string name = "guozhijian";
  3.             ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object o){
  4.                 MessageBox.Show(name);
  5.             }));
  6.         }
複製代碼
在匿名代碼段裏面能夠直接訪問局部變量,不用在關心參數傳遞的問題
二. Invoke
1. this.Invoke
如今,在業務線程裏面執行完畢,要改變窗體控件的值了,此時,若是直接經過this獲得控件的句柄,而後對它進行操做是會拋異常的,.Net WinForm Application裏面是不容許這樣的操做的。這是,能夠調用Invoke方法

2.Invoke方法簽名:
  1. object Control.Invoke(Delegate Method)
  2. object Control.Invoke(Delegate Method, params object[] args)
  3.  
複製代碼
3.使用自定義委託
  1. private void DoWork() {
  2.             WaitCallback wc = new WaitCallback(this.DoSomething);
  3.             ThreadPool.QueueUserWorkItem(wc, "Guozhijian");
  4.         }
  5.  
  6.         private delegate void MyInvokeDelegate(string name);
  7.         private void DoSomething(object o) {
  8.             this.Invoke(new MyInvokeDelegate(this.ChangeText), o.ToString());
  9.         }
  10.  
  11.         private void ChangeText(string name) {
  12.             this.textBox1.Text = name;
  13.         }
複製代碼
哦,太麻煩了,難道我每次都要定義一個委託啊,這樣可不行。

4.使用System.Action
  1.  
  2. private void DoWork() {
  3.             WaitCallback wc = new WaitCallback(this.DoSomething);
  4.             ThreadPool.QueueUserWorkItem(wc, "Guozhijian");
  5.         }
  6.  
  7.         private void DoSomething(object o) {
  8.             this.Invoke(new Action<string>(this.ChangeText), o.ToString());
  9.         }
  10.  
  11.         private void ChangeText(string name) {
  12.             this.textBox1.Text = name;
  13.         }
複製代碼
本例傳遞一個參數, System.Action有不少個重載,能夠無參數(非泛型),而最多能夠有四個參數,一樣採用匿名方法,不使用泛型形式的System.Action,以下:
  1.  
  2. private void DoWork() {
  3.             WaitCallback wc = new WaitCallback(this.DoSomething);
  4.             ThreadPool.QueueUserWorkItem(wc, "Guozhijian");
  5.         }
  6.  
  7.         private void DoSomething(object o) {
  8.             this.Invoke(new Action(delegate() {
  9.                 this.textBox1.Text = o.ToString();
  10.             }));
  11.         }
  12.  
複製代碼
5.使用 System.Func
若是Invoke調用主窗體操做以後,還但願在調用完獲得一個返回值:
  1.  
  2. private void DoWork() {
  3.             WaitCallback wc = new WaitCallback(this.DoSomething);
  4.             ThreadPool.QueueUserWorkItem(wc, "Guozhijian");
  5.         }
  6.  
  7.         private void DoSomething(object o) {
  8.             System.Func&lt;string, int&gt; f = new Func<string, int>(this.GetId);
  9.             object result = this.Invoke(f,o.ToString());
  10.             MessageBox.Show(result.ToString());
  11.         }
  12.  
  13.         private int GetId(string name) {
  14.             this.textBox1.Text = name;
  15.             if (name == "Guozhijian") {
  16.                 return 999;
  17.             }
  18.             else {
  19.                 return 0;
  20.             }
  21.         }
  22.  
複製代碼
result的值爲 999。
System.Func一樣有不少泛形重載,這裏不贅述。

6.關於Invoke的擁有者:Control
本文例中都是用this來引用,這裏this替換爲窗體任何一個控件的句柄都是OK的,由於Control.Invoke含義是將方法委託給擁有該Control的線程去執行。

如下爲示例代碼:
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Data;
  6. using System.Text;
  7. using System.Windows.Forms;
  8. using System.Threading;
  9.  
  10. namespace WindowsFormsApplication
  11. {
  12.     public partial class Form1 : Form
  13.     {
  14.         public Form1()
  15.         {
  16.             InitializeComponent();
  17.         }
  18.         public delegate void InvokeMethodDelegate(string name);
  19.         
  20.         private void button1_Click(object sender, EventArgs e)
  21.         {
  22.             DoWithCommon();
  23.         }
  24.  
  25.         private void DoWithCommon()
  26.         {
  27.             WaitCallback waitCallBack = new WaitCallback(this.InvokeMethod);
  28.             ThreadPool.QueueUserWorkItem(waitCallBack喵那個咪的線程操做嘍~");, "
  29.         }
  30.         private void InvokeMethod(object x)
  31.         {
  32.             this.Invoke(new InvokeMethodDelegate(this.ChangeUIWithCommon), x.ToString());
  33.         }
  34.         private void ChangeUIWithCommon(string name)
  35.         {
  36.             this.button1.Text = name;
  37.         }
  38.  
  39.     }
  40. }
  41.  
複製代碼
相關文章
相關標籤/搜索