Control.Invoke与具有TaskScheduler的任务

问题描述:

我已经看遍了所有,我找不到答案。 是更好,更糟糕的,或漠不关心的使用方法:Control.Invoke与具有TaskScheduler的任务

{ 
... 
RefreshPaintDelegate PaintDelegate = new RefreshPaintDelegate(RefreshPaint); 
Control.Invoke(PaintDelegate); 
} 

protected void RefreshPaint() 
{ 
    this.Refresh(); 
} 

......或者......

Task.Factory.StartNew(() => 
{ 
    this.Refresh(); 
}, 
CancellationToken.None, 
TaskCreationOptions.None, 
uiScheduler); 

假设uiScheduler是一个将调用委托给UI线程的调度程序,我会说在功能上,使用这两者是无关紧要的(除了在调用完成之前对Control.Invoke的调用将阻塞,而对Task的调用不会,但是,您可以始终使用Control.BeginInvoke使它们在语义上相同)。

从语义的角度来看,我会说使用Control.Invoke(PaintDelegate)是一个更好的方法;当使用Task时,您正在做一个隐式声明,指出您希望执行一个工作单元,并且通常该工作单元具有与其他工作单元一起安排的上下文,它是调度程序,用于确定如何委派该工作(通常,它是多线程的,但在这种情况下,它被编组到UI线程中)。还应该说,uiSchedulerControl之间没有明确的联系,链接到UI线程,应该调用一个线程(通常,它们都是相同的,但可能有多个UI线程,尽管很稀少)。

但是,在使用Control.Invoke时,您想要执行的操作的意图很明确,您希望将呼叫整理到Control正在抽取消息的UI线程,并且此调用完全表明。

但我认为最好的选择是使用SynchronizationContext实例;它抽象出您需要将呼叫同步到该上下文的事实,而不是其他两个选项,这些选项对呼叫中的意图(Task)或对于它的完成方式非常具体(Control.Invoke)不明确。

这是不一样的。第一个版本将阻止调用线程,直到UI线程准备调用该方法。对于非阻塞版本,您应该使用Control.BeginInvoke,它也会立即返回。

除此之外(如果您将任务与线程池线程进行比较),使用它们几乎没有什么区别。

[编辑]

在这种情况下,存在Task.Factory.StartNewControl.BeginInvoke(但不Invoke正如我上面写)之间没有差别,因为只有一个单一的GUI线程可以执行代码。无论您使用其中任何一个进行了多少次调用,它们仍将在UI线程变为空闲时依次执行。

+0

但是['BeginInvoke'](http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx)不会。与“Invoke”相同的语法,但它是异步的。 – 2011-04-28 12:07:48

+0

查看更多信息,请参阅[Invoke()和BeginInvoke()](http://*.com/q/229554/616329)之间的区别。 – 2011-04-28 12:10:43

+0

所以Task.Factory.StartNew()会更加开放并行吗?它会安排刷新发生,并允许调用线程继续,对不对? – 2011-04-28 12:10:48