在
WinForm開發中,咱們一般不但願當窗體上點了某個按鈕執行某個業務的時候,窗體就被卡死了,直到該業務執行完畢後才緩過來。一個最直接的方法即是使用多線程。多線程編程的方式在WinForm開發中必不可少。
本文介紹在WinForm開發中如何使用多線程,以及在線程中如何經過Control.Invoke方法返回窗體主線程執行相關操做。
語法:
- public Object Invoke(
- Delegate method
- )
複製代碼
參數
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示例:
-
- using System;
- using System.Drawing;
- using System.Windows.Forms;
- using System.Threading;
-
- public class MyFormControl : Form
- {
- public delegate void AddListItem();
- public AddListItem myDelegate;
- private Button myButton;
- private Thread myThread;
- private ListBox myListBox;
- public MyFormControl()
- {
- myButton = new Button();
- myListBox = new ListBox();
- myButton.Location = new Point(72, 160);
- myButton.Size = new Size(152, 32);
- myButton.TabIndex = 1;
- myButton.Text = "Add items in list box";
- myButton.Click += new EventHandler(Button_Click);
- myListBox.Location = new Point(48, 32);
- myListBox.Name = "myListBox";
- myListBox.Size = new Size(200, 95);
- myListBox.TabIndex = 2;
- ClientSize = new Size(292, 273);
- Controls.AddRange(new Control[] {myListBox,myButton});
- Text = " 'Control_Invoke' example";
- myDelegate = new AddListItem(AddListItemMethod);
- }
- static void Main()
- {
- MyFormControl myForm = new MyFormControl();
- myForm.ShowDialog();
- }
- public void AddListItemMethod()
- {
- String myItem;
- for(int i=1;i<6;i++)
- {
- myItem = "MyListItem" + i.ToString();
- myListBox.Items.Add(myItem);
- myListBox.Update();
- Thread.Sleep(300);
- }
- }
- private void Button_Click(object sender, EventArgs e)
- {
- myThread = new Thread(new ThreadStart(ThreadFunction));
- myThread.Start();
- }
- private void ThreadFunction()
- {
- MyThreadClass myThreadClassObject = new MyThreadClass(this);
- myThreadClassObject.Run();
- }
- }
-
- // The following code assumes a 'ListBox' and a 'Button' control are added to a form,
- // containing a delegate which encapsulates a method that adds items to the listbox.
-
- public class MyThreadClass
- {
- MyFormControl myFormControl1;
- public MyThreadClass(MyFormControl myForm)
- {
- myFormControl1 = myForm;
- }
-
- public void Run()
- {
- // Execute the specified delegate on the thread that owns
- // 'myFormControl1' control's underlying window handle.
- myFormControl1.Invoke(myFormControl1.myDelegate);
- }
- }
-
複製代碼
關於線程個數問題:一個線程默認分配堆棧是
1m
一個進程最大佔
2G;因此致使一個進程內最大線程是2024個理論線程。
固然確定不是了。若是編譯的時候,能夠設置每一個線程的最大堆棧的佔用。
多線程編程:一個是本身開一個線程。一個是把任務交給線程池。交給線程池的就是要求交給的是委託;定義一個委託的實質是:定義一種自定義的消息類型,
這樣這個消息類型定義後,能夠註冊與這個消息類型對應得處理過程,註冊方法是:實例化委託或者給定義事件的實例化對象綁定想應的處理過程;當事件發生時會把消息傳給這個過程;也就是指針指向這個實例化時候或者綁定時候的處理過程;從而實現委託的目的。在使用委託的時候,注意事項是線程間的安全問題,也就是若是委託要操做的是控件的屬性,那就須要經過控件自己經過調用這個委託,調用方式是有規定的,用的是:
invoke()從而避免了生成控件的線程和操做控件的線程之間的衝突。由於
Control.Invoke含義是將方法委託給擁有該Control的線程去執行
-
. WinForm多線程編程
1. new Thread()
新開一個線程,執行一個方法,沒有參數傳遞:
- private void DoWork() {
- Thread t = new Thread(new ThreadStart(this.DoSomething));
- t.Start();
- }
- private void DoSomething() {
- MessageBox.Show("thread start");
- }
複製代碼
新開一個線程,執行一個方法,並傳遞參數:
- private void DoWork() {
- Thread t = new Thread(new ParameterizedThreadStart(this.DoSomething));
- t.Start("guozhijian");
- }
- private void DoSomething(object o) {
- MessageBox.Show(o.ToString());
- }
複製代碼
參數定義爲
object類型。
2. ThreadPool
衆所周知,新開一個線程代價是很高昂的,若是咱們每一個操做都新開一個線程,那麼太浪費了,因而,下面使用線程池。
無參數傳遞:
- private void DoWork() {
- ThreadPool.QueueUserWorkItem(new WaitCallback(this.DoSomething));
- }
- private void DoSomething(object o) {
- MessageBox.Show("thread start");
- }
複製代碼
有參數傳遞:
- private void DoWork() {
- ThreadPool.QueueUserWorkItem(new WaitCallback(this.DoSomething), "guozhijian");
- }
- private void DoSomething(object o) {
- MessageBox.Show(o.ToString());
- }
複製代碼
使用匿名方法更靈活:
- private void DoWork() {
- string name = "guozhijian";
- ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object o){
- MessageBox.Show(name);
- }));
- }
複製代碼
在匿名代碼段裏面能夠直接訪問局部變量,不用在關心參數傳遞的問題
二. Invoke
1. this.Invoke
如今,在業務線程裏面執行完畢,要改變窗體控件的值了,此時,若是直接經過this獲得控件的句柄,而後對它進行操做是會拋異常的,.Net WinForm Application裏面是不容許這樣的操做的。這是,能夠調用Invoke方法
2.Invoke方法簽名:
- object Control.Invoke(Delegate Method)
- object Control.Invoke(Delegate Method, params object[] args)
-
複製代碼
3.使用自定義委託
- private void DoWork() {
- WaitCallback wc = new WaitCallback(this.DoSomething);
- ThreadPool.QueueUserWorkItem(wc, "Guozhijian");
- }
-
- private delegate void MyInvokeDelegate(string name);
- private void DoSomething(object o) {
- this.Invoke(new MyInvokeDelegate(this.ChangeText), o.ToString());
- }
-
- private void ChangeText(string name) {
- this.textBox1.Text = name;
- }
複製代碼
哦,太麻煩了,難道我每次都要定義一個委託啊,這樣可不行。
4.使用System.Action:
-
- private void DoWork() {
- WaitCallback wc = new WaitCallback(this.DoSomething);
- ThreadPool.QueueUserWorkItem(wc, "Guozhijian");
- }
-
- private void DoSomething(object o) {
- this.Invoke(new Action<string>(this.ChangeText), o.ToString());
- }
-
- private void ChangeText(string name) {
- this.textBox1.Text = name;
- }
複製代碼
本例傳遞一個參數,
System.Action有不少個重載,能夠無參數(非泛型),而最多能夠有四個參數,一樣採用匿名方法,不使用泛型形式的System.Action,以下:
-
- private void DoWork() {
- WaitCallback wc = new WaitCallback(this.DoSomething);
- ThreadPool.QueueUserWorkItem(wc, "Guozhijian");
- }
-
- private void DoSomething(object o) {
- this.Invoke(new Action(delegate() {
- this.textBox1.Text = o.ToString();
- }));
- }
-
複製代碼
5.使用
System.Func
若是Invoke調用主窗體操做以後,還但願在調用完獲得一個返回值:
-
- private void DoWork() {
- WaitCallback wc = new WaitCallback(this.DoSomething);
- ThreadPool.QueueUserWorkItem(wc, "Guozhijian");
- }
-
- private void DoSomething(object o) {
- System.Func<string, int> f = new Func<string, int>(this.GetId);
- object result = this.Invoke(f,o.ToString());
- MessageBox.Show(result.ToString());
- }
-
- private int GetId(string name) {
- this.textBox1.Text = name;
- if (name == "Guozhijian") {
- return 999;
- }
- else {
- return 0;
- }
- }
-
複製代碼
result的值爲
999。
System.Func一樣有不少泛形重載,這裏不贅述。
6.關於Invoke的擁有者:Control
本文例中都是用this來引用,這裏this替換爲窗體任何一個控件的句柄都是OK的,由於Control.Invoke含義是將方法委託給擁有該Control的線程去執行。
如下爲示例代碼:
-
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Text;
- using System.Windows.Forms;
- using System.Threading;
-
- namespace WindowsFormsApplication
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
- public delegate void InvokeMethodDelegate(string name);
-
- private void button1_Click(object sender, EventArgs e)
- {
- DoWithCommon();
- }
-
- private void DoWithCommon()
- {
- WaitCallback waitCallBack = new WaitCallback(this.InvokeMethod);
- ThreadPool.QueueUserWorkItem(waitCallBack喵那個咪的線程操做嘍~");, "
- }
- private void InvokeMethod(object x)
- {
- this.Invoke(new InvokeMethodDelegate(this.ChangeUIWithCommon), x.ToString());
- }
- private void ChangeUIWithCommon(string name)
- {
- this.button1.Text = name;
- }
-
- }
- }
-
複製代碼