现在假设我有这样一个窗体(包含一个进度条和一个按钮与两个文本框),在第一个文本框中输入一个数字进行阶乘运算,在此过程中进度条与运算进度保持一致,同时可以在第二个文本框中进行其它工作(比如输入)。对付这样的题目,除了使用BackGroundWorker之外还可以使用异步Invoke来完成:
首先让我们看看界面以及对应的代码:
【界面】

【代码】

[C#]
namespace CSharp
{
    public partial class Form1 : Form
    {
        long result = 1;

long PGo(int endnum)
        {
            while(progressBar1.Value<=endnum)
            {
                result *= endnum;
                progressBar1.Value += 1;
                Thread.Sleep(1000);
                endnum--;
            }
            return result;
        }

public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
           
        }

private void button1_Click(object sender, EventArgs e)
        {
            progressBar1.Maximum = Convert.ToInt32(textBox1.Text);
            Func<int,long> a = new Func<int,long>(PGo);

//BeginInvoke先启动后台线程做循环
            var result = a.BeginInvoke
                (
                Convert.ToInt32(textBox1.Text),
                //如果完成了循环,那么执行此委托
                    delegate(IAsyncResult r)
                    {
                        if (r.IsCompleted)
                        {
                            MessageBox.Show("Finished!");
                            textBox1.Text = (r.AsyncState as Func<int, long>).EndInvoke(r).ToString();
                        }
                    },a
                );

//不能使用result.EndInvoke,此方法将导致当前进程宕住,直到后台线程完毕为止。因为WinForm主线程不会自动关闭,所以
            //无需此线程,但是控制台程序必须要!因为控制台进程“瞬间即逝”。
        }
    }
}
[VB.NET]
Namespace CSharp
    Public Partial Class Form1
        Inherits Form
        Private result As Long = 1

Private Function PGo(endnum As Integer) As Long
            While progressBar1.Value <= endnum
                result *= endnum
                progressBar1.Value += 1
                Thread.Sleep(1000)
                endnum -= 1
            End While
            Return result
        End Function

Public Sub New()
            InitializeComponent()
        End Sub
        Private Sub Form1_Load(sender As Object, e As EventArgs)

End Sub

Private Sub button1_Click(sender As Object, e As EventArgs)
            progressBar1.Maximum = Convert.ToInt32(textBox1.Text)
            Dim a As New Func(Of Integer, Long)(AddressOf PGo)

'BeginInvoke先启动后台线程做循环
            '如果完成了循环,那么执行此委托 www.2cto.com
            Dim result = a.BeginInvoke(Convert.ToInt32(textBox1.Text), Function(r As IAsyncResult) Do
                If r.IsCompleted Then
                    MessageBox.Show("Finished!")
                    textBox1.Text = TryCast(r.AsyncState, Func(Of Integer, Long)).EndInvoke(r).ToString()
                End If
            End Function, a)
            '不能使用result.EndInvoke,此方法将导致当前进程宕住,直到后台线程完毕为止。因为WinForm主线程不会自动关闭,所以
            '无需此线程,但是控制台程序必须要!因为控制台进程“瞬间即逝”。
        End Sub
    End Class
End Namespace
解释一些关键部分:
1)BeginInvoke:此方法将“异步”执行委托所指向的那个方法。所谓“异步”,就是结果并不是像调用“Invoke”方法一样直接就出现结果,BeginInvoke将在内部开辟一个新线程(注意:是后台线程!)去执行这个委托方法。因为是后台线程,因此如果主程序一旦关闭或者停止,无论后台线程的任务是否执行完毕,都将自动终止。本示例因为是WinForm,一旦运行主线程不会自动结束,但是对于诸如控制台一类的程序则不然,因此控制台程序如果使用BeginInvoke方法,必须要在其后面调用对应的EndInvoke。EndInvoke方法会阻塞当前进程,直至BeginInvoke执行完毕所有的委托方法后直接返回一个结果。如:
[C#]
namespace CSharp
{
    public class Program
    {
        int Fun()
        {
            Console.WriteLine("This is a fun function with a return value……");
            return 1;
        }

static void Main(string[] args)
        {
            Func<int> fun = new Program().Fun;
            //此处创建了一个新线程,都是后台线程运行
            var r =fun.BeginInvoke(null, null);
            //此处Thread.Sleep(10)不可以省略,否则后台线程没有机会得到执行
            while (!r.IsCompleted) { Thread.Sleep(10); }
            //此处不可以省略,因为任何后台线程依附于主线程。一旦主线程停止以后后台线程自动关闭。
            Console.WriteLine(fun.EndInvoke(r));
        }
    }
}
[VB.NET]
Namespace CSharp
    Public Class Program
        Private Function Fun() As Integer
            Console.WriteLine("This is a fun function with a return value……")
            Return 1
        End Function

Private Shared Sub Main(args As String())
            Dim fun As Func(Of Integer) = AddressOf New Program().Fun
            '此处创建了一个新线程,都是后台线程运行
            Dim r = fun.BeginInvoke(Nothing, Nothing)
            '此处Thread.Sleep(10)不可以省略,否则后台线程没有机会得到执行
            While Not r.IsCompleted
                Thread.Sleep(10)
            End While
            '此处不可以省略,因为任何后台线程依附于主线程。一旦主线程停止以后后台线程自动关闭。
            Console.WriteLine(fun.EndInvoke(r))
        End Sub
    End Class
End Namespace
注意以上多了“While……”的判断部分——因为BeginInvoke返回一个IAsyncResult的接口,其中包含了一个IsCompleted布尔属性:该属性指示新开辟的线程是否已经把委托的全部代码执行完毕了。由于是异步(要检测新线程的完成情况),我们无法直接从主线程获取此情况,自然只能在主线程中对IsCompleted做类似“死循环”的判断,并且在其中增加Sleep代码(此不能省略,否则BeginInvoke子线程没有机会被执行,都被主线程卡死了)。当IsCompleted=True之后,立即执行EndInvoke输出结果。
当然,其实while这一段完全可以省略,不过这样看上去更为正式一些;即便没有while去显示判断IsCompleted属性,EndInvoke也会阻塞主线程继续进行——直到执行完毕输出结果为止。
另外:While……这一段和EndInvoke不可以都省略,否则无法输出任何结果!(因为在控制台中,不调用EndInvoke主线程不会被阻塞,一旦执行完毕自然终止程序,后台线程也完蛋了)。
同时,在WinForm那个示例中你还注意到了使用IAsyncResult接口参数和一个object的用法——BeginInvoke通常是自动根据委托生成的,大致如下:
BeginInvoke(参数1,……参数N,IAsyncResult,objectvalue)
1)参数1……参数N:都是可选参数,取决于你原先委托定义时是否有参数,以及多少参数(如果委托只需要一个参数,那么自生成的BeginInvoke也只有一个参数)。
2)IAsyncResult,当BeginInvoke方法执行完毕之后,内部会发出一个信号通知IAsyncResult已经执行完毕了(IAsyncResult的IsCompleted也同步设置为True)。
3)objectvalue:可选参数。
因为前面说过——BeginInvoke执行完毕之后(当IsCompleted=True时),EndInvoke就被自动执行生成结果。因此我们通过把objectvalue设置成委托实例传入BeginInvoke,然后在IAsyncResult中执行EndInvoke就可以完成任务。
因为WinForm主线程不会自动终止,所以不能像控制台一样在外面调用EndInvoke,否则主界面会宕机(直到异步方法全部执行完毕),应该在IAsyncResult中执行)。

使用委托的BeginInvoke方法来完成复杂任务的操作的更多相关文章

  1. 委托的BeginInvoke和EndInvoke方法

    .NET Framework 允许异步调用任何方法,为了实现异步调用目标,需要定义与被调用方法具有相同签名的委托.公共语言运行时会自动使用适当的签名为该委托定义 BeginInvoke 和 EndIn ...

  2. 委托的多线程方法BeginInvoke

    同步方法和异步方法: 同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果.(比如烧水泡茶,需要等水烧开了才能继续泡茶) 异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执 ...

  3. 异步使用委托delegate --- BeginInvoke和EndInvoke方法

    当我们定义一个委托的时候,一般语言运行时会自动帮委托定义BeginInvoke 和 EndInvoke两个方法,这两个方法的作用是可以异步调用委托. 方法BeginInvoke有两个参数: Async ...

  4. 异步编程(AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用小总结)

    http://www.cnblogs.com/panjun-Donet/archive/2009/03/03/1284700.html 让我们来看看同步异步的区别: 同步方法调用在程序继续执行之前需要 ...

  5. 控件的invoke和beginInvoke方法

    System.Windows.Forms.Timer 的timer是在主线程上执行的,因此在timer的tick事件中操作界面上的控件不会发生线程的安全性检测. Control的invoke和begi ...

  6. C# 对委托的BeginInvoke,EndInvoke 及Control 的BeginInvoke,EndInvoke 的理解

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  7. [转载]Winform中Control的Invoke与BeginInvoke方法

    转自http://www.cppblog.com/baby-fly/archive/2010/04/01/111245.html 一.为什么 Control类提供了 Invoke和 BeginInvo ...

  8. delegate的Invoke和BeginInvoke方法

    C#中的控件和delegate委托方法都有Invoke和BeginInvoke方法,控件的这两个方法网上讲得很多, 这里就不多说了,下面讲一下delegate的Invoke和BeginInvoke方法 ...

  9. 转:C# 对委托的BeginInvoke,EndInvoke 及Control 的BeginInvoke,EndInvoke 的理解

    转载自:http://www.cnblogs.com/easyfrog/p/3141269.html using System; using System.Collections.Generic; u ...

随机推荐

  1. HttpClient使用笔记

    使用版本为4.5.1 常用API: 1.获取网页内容:InputStream in = response.getEntity().getContent() 2.获取状态码:response.getSt ...

  2. 35 个必须有的Bootstrap工具和生成器

    Bootstraptor If you think that bootstrap templates are not enough for you, you should go with bootst ...

  3. 如何在eclipse中配置Selenium

    1, Install python 33.(Python 27也可以) 2, Setup Selenium If you did not install Easy_install module, yo ...

  4. android.support.v4.widget.DrawerLayout使用

    activity_main.xml布局如下: <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas ...

  5. W25Q32的使用

    一.W25Q32简介 W25Q32是华邦公司推出的大容量“SPI  FLASH” 产品. 1.容量 32M-Bit/4M-byte(4,194,304) 2.存储结构 页:256-bytes 扇区:4 ...

  6. MVC中Controller和Action讲解上篇

    一般我们用mvc开发程序时一般需要三个步骤, 创建模型.创建控制器.创建视图 之前开发程序都是按照这样的步骤来开发的,也没有想过mvc的原理,比如route是怎么找到controller的,contr ...

  7. 微信支付-b

    微信支付 APP端开发步骤(传送门):https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5 1.首先下载最新的微信支付的SDK包 ...

  8. Jquery Highcharts 选项配置 说明文档

    Highcharts提供大量的选项配置参数,您可以轻松定制符合用户要求的图表,下面为Highcharts常用的最核心的参数选项配置. Chart:图表区选项 Chart图表区选项用于设置图表区相关属性 ...

  9. 简单3d RPG游戏 之 004 攻击(一)

    功能:实现点击键盘F键,怪物血量条减少,并且假定是近战,需要对距离进行判断,距离小于一定值的时候按F才会减少怪物的血条. 新建c#脚本PlayerAttack,绑定到Player,并在unity里将敌 ...

  10. Notepad++ 书签

      Notepad++,有一个书签功能,指定书签是Ctrl+F2,在书签之间移动是按F2来切换,这个可以在几个想查看的数据之间进行快速切换,所以看起来就很方便.