• 使用委托的BeginInvoke方法来完成复杂任务的操作


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

  • 相关阅读:
    软件测试七年之痒,依然热爱!我还是从前那个少年!
    我想从功能测试转向自动化测试,怎么办?
    python中的一些内置函数
    python中eval()
    集合
    列表的切片:取出来还是一个列表,可用在复制列表元素的操作
    字符串常用的方法
    dict字典,以及字典的一些基本应用
    list列表(也叫数组),以及常用的一些方法
    jsonpath的用法
  • 原文地址:https://www.cnblogs.com/rr163/p/4223133.html
Copyright © 2020-2023  润新知