面试问题:当Control.InvokeRequired时,您使用Control.Invoke还是Control.BeginInvoke?

6
最近我经历了一次非常糟糕的面试,他们用好警察/坏警察的方式与我交流。无论我怎么回答都不足够让其中一个人满意,我的信心在逐渐减少。最后让我真正困惑的是他的最后一个问题:

如果控件需要InvokeRequired,在使用.Invoke或.BeginInvoke会有什么区别呢?

让我给你举个例子,以便清晰理解:

public delegate string WorkLongDelegate(int i);

var del = new WorkLongDelegate(WorkLong);
var callback = new AsyncCallback(CallBack);
del.BeginInvoke(3000, callback, del);

public string WorkLong(int i)
{
      Thread.Sleep(i);
      return (string.Format("Work was done within {0} seconds.", i));            
}

private void CallBack(IAsyncResult ar)
{
    var del = (WorkLongDelegate) ar.AsyncState;
    SetText2(del.EndInvoke(ar));
}

private void SetText2(string s)
{
   if(InvokeRequired)
   {
       // What is the difference between BeginInvoke and Invoke in below?
       BeginInvoke(new MethodInvoker(() => textBox1.Text = s)); 
   }
   else
   {
       textBox1.Text = s;
   }
}

我曾经提到BeginInvoke可以异步执行,而Invoke会在执行之前阻塞UI线程。但这还不够好。即便如此,我不理解如果使用Invoke会有什么性能影响。

请问有人可以为我解答一下吗?

3个回答

14

Invoke 不会停止 UI 线程。它会阻塞调用线程,直到UI线程已完成。

所以问题实际上是你是否希望在UI完成更新之前继续进行后台操作。通常情况下,我认为是这样的 - 例如,如果你只是向UI提供进度报告,你不想因为UI线程还没有赶上来而停止工作。

另一方面,如果你需要从UI线程中获取某些内容(尽管这很少见),那么你可能需要使用Invoke。我建议你使用BeginInvoke,除非你有特殊的原因要使用Invoke。但无论哪种方式,你应该明白其中的区别 :)


2
因此,Invoke 在您的线程安全设置方面容易发生死锁。 - Mark Sowul

4
使用Invoke()的一个非常明显的用例是当您需要调用一个需要返回值的方法时。只有Invoke()可以为您提供这个功能,它返回Object,即方法的返回值。
另一个较弱的用例是,当您的工作线程产生结果的速度远远快于UI线程跟得上的速度时。使用Invoke()会限制工作线程,而且调用列表不会无限增长。然而,这只是为了解决更大的问题而采取的权宜之计,没有必要更新UI得比人眼能感知的速度还要快。每40毫秒更新一次看起来很平滑。如果UI线程仍然需要太多时间来处理结果集,则仍然需要使用Invoke()。出现这种问题的经典迹象是UI线程冻结,无法进行绘制并响应鼠标和键盘事件,因为它被调用请求完全压垮了。而且,工作线程完成运行后,UI线程在一段时间内仍然保持无响应状态,忙于处理积压的任务。
另一个情况是您传递给BeginInvoke()的对象需要锁定。当使用Invoke()时不需要锁定,UI线程和工作线程不能同时访问对象。但是对于BeginInvoke,它会继续执行,如果线程继续使用同一个对象,则必须在UI线程和工作线程中都使用锁定来保护对象。如果锁定阻止了工作线程的任何进展,那么您可能还不如使用Invoke()。这种情况非常罕见,主线程需要花费大量时间才能开始执行委托。并且最好在调用后创建对象的一个新实例,以便无需锁定。

0

如果使用异步版本,您的后台线程可以立即继续执行,而无需等待上下文切换。

通常情况下,这应该会更快(对于后台线程),根据我的理解。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接