代码之家  ›  专栏  ›  技术社区  ›  Martin R-L

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

  •  0
  • Martin R-L  · 技术社区  · 14 年前

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

    我想要以下内容:

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

    到目前为止,(单线程)代码如下所示:

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

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

    Workdays 是一个 IEnumerable<DateTime> ForEach 做什么 前额 属于 List<T> 是的,但是来自的扩展方法 MoreLINQ .

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

    4 回复  |  直到 14 年前
        1
  •  1
  •   Bastiaan Linders    14 年前

    在单独的线程中运行foreach循环以及对控件(窗体)调用的回调将是我的解决方案。

    下面的链接包含一个很好的例子。 http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

        2
  •  1
  •   Nenad Dobrilovic    14 年前

    您应该在单独的线程(而不是主GUI线程)中进行长时间运行的计算,以便具有消息循环的主线程可以是空闲的。 在for each循环的内部,您应该使用 Control.Invoke 方法。您可以选择在被动视图中包装方法,如下所示:

    public void DoSomething() {
        if (this.InvokeRequired) {
            this.Invoke(DoSomethingDelegate);
            ...
        }
    }
    

    或者,使用类似这样的方法来代替标准方法调用:

    myFormControl1.Invoke(myFormControl1.myDelegate);
    
        3
  •  1
  •   Martin R-L    14 年前

    +1向尼纳德和巴斯蒂安指指我 Control.Invoke 方向。

    为了充分获得“被动观点”模式的好处,我不想知道 Control _~中的winforms类型 supervising controller _»(该类型只能由视图接口的实现者知道,即派生自 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 是的。

        4
  •  0
  •   Chris Marisic    14 年前

    另一种选择是利用plinq-并行linq

    http://msdn.microsoft.com/en-us/magazine/cc163329.aspx

    你可以做点什么

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

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