现在假设我有这样一个窗体(包含一个进度条和一个按钮与两个文本框),在第一个文本框中输入一个数字进行阶乘运算,在此过程中进度条与运算进度保持一致,同时可以在第二个文本框中进行其它工作(比如输入)。对付这样的题目,除了使用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. Android图像处理2

    此次实验主要通过Android中的方法获取输入的颜色矩阵的值,更改后赋值给图片中的颜色矩阵更改图片效果.具体的布局的方法跟笔记1种差不多,只不过这里要添加一个供用户输入的GridView <Gr ...

  2. LM算法

    最小二乘法的概念 最小二乘法的目标:求误差的最小平方和,对应有两种:线性和非线性. 线性最小二乘的解是closed-form即x=(A^T A)^{-1}A^Tb, 而非线性最小二乘没有closed- ...

  3. Python 中的引用和类属性的初步理解

    最近对Python 的对象引用机制稍微研究了一下,留下笔记,以供查阅. 首先有一点是明确的:「Python 中一切皆对象」. 那么,这到底意味着什么呢? 如下代码: #!/usr/bin/env py ...

  4. 【转】STL中mem_fun和mem_fun_ref的用法及区别

    原文:http://www.cppblog.com/mysileng/archive/2012/12/25/196615.html 引子: 怎么对容器中的所有对象都进行同一个操作?我们可能首先想到的是 ...

  5. OOP三类继承的区别

    OOP继承的区别提纲: 1. 普通类继承,并非一定要重写父类方法.2. 抽象类继承,如果子类也是一个抽象类,并不要求一定重写父类方法.如果子类不是抽象类,则要求子类一定要实现父类中的抽象方法.3. 接 ...

  6. WRS是什么?

    全球参考系(WRS)是为卫星下行数据服务而建立的一种全球符号坐标系统,本文详细介绍了Landsat卫星的轨道特性,给出了相应的WRS网格坐标位置的估算方法,并给出了估算的结果。对该方法的研究为地面应用 ...

  7. Linux的安全模式

    今天尝试了一下开机启动,在rc.local中进行设置,但是我写的java -jar transport.jar是一个Hold处理,无法退出:导致开机的时候一直停留在等待页面. 处理机制: 1. 在Li ...

  8. iOS 的UINavigationController详解与使用添加UIBarButtonItem

    转发自:http://blog.csdn.net/totogo2010/article/details/7681879 分类: iOS开发入门2012-06-21 11:10 53077人阅读 评论( ...

  9. (转载)Cocos2dx-OpenGL ES2.0教程:使用VBO索引(4)

    在上一篇文章中,我们介绍了uniform和模型-视图-投影变换,相信大家对于OpenGL ES 2.0应该有一点感觉了.在这篇文章中,我们不再画三角形了,改为画四边形.下篇教程,我们就可以画立方体了, ...

  10. jquery pass parameter to ajax callback

    $('.del').on('click', function () { var id = $(this).attr('id'); var url = '/m/g2_content_del/' + id ...