我有一个WinForm «Passive View»和一个«Controller»,其中控制器为列表中的每个元素运行一个长时间运行的函数。
我想要以下内容:
(单线程)代码现在看起来像这样:
View.DateSpan.Workdays.ForEach(
d => {
var processRunInfo = _processRunner.Run( configFile, d );
UdateViewFrom( processRunInfo );
} );
上面的代码“有效”但导致视图冻结,因为它使用了相同的线程,并且它会批量更新视图。
Workdays
是IEnumerable<DateTime>
,而ForEach
执行ForEach
List<T>
所做的事情,但是来自MoreLINQ的扩展方法。
_processRunner.Run
使用提供的参数运行外部命令行应用程序。
答案 0 :(得分:1)
在一个单独的线程中运行foreach循环,回调你的控件(窗体)的Invoke将是我的解决方案。
以下链接包含一个不错的示例。 http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
答案 1 :(得分:1)
您应该在单独的线程(而不是主GUI线程)中进行长时间运行的计算,以便具有消息循环的主线程可以是空闲的。 在for-each循环内部,您应该使用Control.Invoke方法将UpdateViewForm方法封送到GUI线程。您可以选择在PassiveView中包装方法,如下所示:
public void DoSomething() {
if (this.InvokeRequired) {
this.Invoke(DoSomethingDelegate);
...
}
}
或者只是,而不是使用标准方法调用,使用类似这样的东西:
myFormControl1.Invoke(myFormControl1.myDelegate);
答案 2 :(得分:1)
+1给Nenad和Bastiaan,指我走向Control.Invoke
方向。
为了完全获得“被动视图”模式的好处,我不希望在{supervising controller»中知道Control
类型的WinForms(该类型应该只是由视图接口的实现者知道,即从Form
派生的那个。
以下是我如何解决这个令人满意的问题:
Thread
循环创建一个新的ForEach
实例作为参数,然后启动创建的实例。在:
View.DateSpan.Workdays.ForEach(d =>
{
// do stuff...
} );
后:
new Thread( () => View.DateSpan.Workdays.ForEach( d =>
{
// do stuff...
} ) ).Start();
Invoke
。请参阅以下代码。之前:
public string Status
{
set { _statusLabel.Text = value ); }
}
后:
public string Status
{
set { ExecuteOnUIThread( _statusLabel, () => _statusLabel.Text = value ); }
}
辅助方法:
private static void ExecuteOnUIThread( Control control, Action action )
{
if ( control.InvokeRequired )
{
control.Invoke( action );
}
else
{
action();
}
}
生产WinForm视图就像一个魅力,并且添加了一个while循环来旋转线程,而后台线程在我的BDD故事中工作,所以他们用我的旧视图«test spy»
答案 3 :(得分:0)
另一种选择是利用PLINQ - Parallel Linq
http://msdn.microsoft.com/en-us/magazine/cc163329.aspx
你可以做点什么
View.DateSpan.Workdays.AsParallel().ForEach(
d => {
var processRunInfo = _processRunner.Run( configFile, d );
UdateViewFrom( processRunInfo );
} );
您仍然很可能必须在回调事件中处理返回更新。