线程池学习

 

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.  
复制代码
相关文章
相关标签/搜索