在
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;
- }
-
- }
- }
-
复制代码