如何为不同线程的列表中的每个元素顺序运行一个函数,并在每个函数运行后更新(被动)视图?

时间:2010-02-25 13:11:00

标签: c# multithreading

我有一个WinForm «Passive View»和一个«Controller»,其中控制器为列表中的每个元素运行一个长时间运行的函数。

我想要以下内容:

  • 功能应按顺序运行
  • 循环列表并运行函数时,视图不得冻结
  • 每个函数运行后,视图将使用运行结果更新

(单线程)代码现在看起来像这样:

View.DateSpan.Workdays.ForEach( 
   d => {
           var processRunInfo = _processRunner.Run( configFile, d );

           UdateViewFrom( processRunInfo );
         } );

上面的代码“有效”但导致视图冻结,因为它使用了相同的线程,并且它会批量更新视图。

WorkdaysIEnumerable<DateTime>,而ForEach执行ForEach List<T>所做的事情,但是来自MoreLINQ的扩展方法。

_processRunner.Run使用提供的参数运行外部命令行应用程序。

4 个答案:

答案 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 );
         } );

您仍然很可能必须在回调事件中处理返回更新。