C# Control的Invoke和BeginInvoke方法的区别

首先我们要清楚Delegate.Invoke/BeginInvoke和Control.Invoke/BeginInvoke是不同的,他们的区别,简单理解如下:

  • Delegate.Invoke:在同一个线程上同步执行。
  • Delegate.BeginInvoke:在线程池线程上异步执行。
  • Control.Invoke:在创建控件的基础窗口句柄的线程上执行委托。
  • Control.BeginInvoke:在创建控件的基础句柄的线程上异步执行委托。

在这里,我们重点讨论的是Control中的Invoke/BeginInvoke的区别。

Invoke和BeginInvoke的执行线程

上面的表述不好理解(这也是MSDN翻译成中文的通病),下面我们通过代码,查看一下执行Invoke的线程:

private void Button1_Click(object sender, EventArgs e)
{
    InvokeThreadName();
}
private void InvokeThreadName()
{
    Thread.CurrentThread.Name = "UIThread";
    Thread th = new Thread(CallInvoke)
    {
        Name = "InvokeThreadName",
        IsBackground=true
    };
    th.Start();
}
private void CallInvoke()
{
    this.Invoke(new Action(PrintCurrentThread));
}

在调试的输出界面中输出:

UIThread

因此,虽然我们通过其他线程调用窗体的的Invoke方法,但是invoke方法里面的代码依然是通过UI线程执行的。

下面我们在查看执行BeginInvoke的线程:

private void Button1_Click(object sender, EventArgs e)
{
    //InvokeThreadName();
    BeginInvokeThreadName();
}
private void BeginInvokeThreadName()
{
    Thread.CurrentThread.Name = "UIThread";
    Thread th = new Thread(CallBeginInvoke)
    {
        Name = "BeginInvokeThreadName"
    };
    th.Start();
}
private void CallBeginInvoke()
{
    this.BeginInvoke(new Action(PrintCurrentThread));
}
private void PrintCurrentThread()
{
    Console.WriteLine(Thread.CurrentThread.Name);
}

在调试的输出界面中输出依然是:

UIThread

也就是说BeginInvoke方法里面的代码也是通过UI线程执行的。

既然都是在UI线程中执行的,那么Invoke和BeginInvoke到底有什么区别了?

Invoke和BeginInvoke的执行顺序

我们通过代码查看一下Invoke代码的执行顺序:

private void Button1_Click(object sender, EventArgs e)
{
    //InvokeThreadName();
    //BeginInvokeThreadName();
    TestInvoke();
}

private void TestInvoke()
{
    Console.WriteLine("开始");
    Thread th = new Thread(CallBeginInvoke)
    {
        Name = "BeginInvokeThreadName"
    };
    th.Start();        
    Console.WriteLine("结束");
}
private void TestInvokeMethod()
{
    this.Invoke(new Action(() =>
    {
        Thread.Sleep(1000);
        Console.WriteLine("这是一个耗时过程");
    }));
}

在调试的输出界面中输出顺序为:

开始
这是一个耗时过程
结束

也就是说,在使用调用Invoke方法时,会阻塞UI线程,同时Invoke下面的代码需要等待Invoke内部的代码执行完之后才可以执行。

接下来我们再通过代码查看一下BeginInvoke代码的执行顺序:

private void Button1_Click(object sender, EventArgs e)
{
    //InvokeThreadName();
    //BeginInvokeThreadName();
    //TestInvoke();
    TestBeginInvoke();
}

private void TestBeginInvoke()
{
    Console.WriteLine("开始");
    this.BeginInvoke(new Action(() =>
    {
        Thread.Sleep(30000);
        Console.WriteLine("这是一个耗时过程");
    }));
    Console.WriteLine("结束");
}

在调试的输出界面中输出顺序变为:

开始
结束
这是一个耗时过程

点击窗体上的Button按钮后,在没有输出“这是一个耗时过程”的过程中,我们无法拖动窗体的,说明在调用BeginInvoke方法时,依然会阻塞UI线程,但是它是异步执行BeginInvoke中的代码,UI线程中的代码执行顺序不受BeginInvoke的影响。

小结

  • Control的Invoke和BeginInvoke的方法都是在UI线程上执行。
  • Invoke方法会阻塞UI线程中Invoke方法后面代码的执行,BeginInvoke方法则不会。
  • 千万不要通过Invoke和BeginInvoke方法执行一个耗时过程,否则会使界面卡死。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值