如何使用Parallel.ForEach循环进入DataGridViewRow

时间:2019-04-04 03:22:12

标签: vb.net parallel.foreach

下面有一个代码,它循环遍历datagridview中的每一行,该视图包含服务器和服务名称,并使用ServiceController引用检查每个服务的状态并返回单元格上的值。

        For Each dgvrow As DataGridViewRow In DataGridView1.Rows
        myController = New ServiceController With {
        .MachineName = dgvrow.Cells(0).Value,
        .ServiceName = dgvrow.Cells(1).Value
        }
        dgvrow.Cells(2).Value = myController.Status.ToString
        Next

这有效,但它顺序运行,每个线程在进入下一行之前都需要花费一些时间,因此我想并行运行它。

我在这里搜索并偶然发现了Parallel.ForEach,但是我找不到合适的代码/组合来完成这项工作。

我最初的尝试是

Parallel.ForEach(dgvrow as DataGridViewRow in DatagridView1.Rows
Sub(myServer)
    myController = New ServiceController With {
        .MachineName = dgvrow.Cells(0).Value,
        .ServiceName = dgvrow.Cells(1).Value
        }
        dgvrow.Cells(2).Value = myController.Status.ToString
End Sub
)

这绝对是错误的,不确定在ForEach部分之后要放什么

预期结果如下所示,我希望“服务状态”列会同时填满。

<table border=1>
  <tr>
    <th>Server Name</th>
    <th>Service Name</th> 
    <th>Service Status</th>
  </tr>
  <tr>
    <th>Server 1</th>
    <th>Service 1</th> 
    <th>Not Running</th>
  </tr>
  <tr>
    <th>Server 2</th>
    <th>Service 2</th> 
    <th>Running</th>
  </tr>
  <tr>
    <th>Server 3</th>
    <th>Service 3</th> 
    <th>Not Running</th>
  </tr>
  </table>

1 个答案:

答案 0 :(得分:0)

DataGridView是UI元素-不允许从UI之外的任何线程访问它。您不能在其上使用Parallel.ForEach

您可以做的是将数据提取为非UI对象,在该对象上并行工作,然后将结果分配回去。

尝试一下:

'UI thread
Dim inputs = DataGridView1.Rows.Cast(Of DataGridViewRow).Select(Function(dgvrow) New With _
{
    .MachineName = dgvrow.Cells(0).Value,
    .ServiceName = dgvrow.Cells(1).Value
}).ToArray()

'Parallel
Dim serviceControllers = inputs.AsParallel().Select(Function (x) New ServiceController With _
{
    .MachineName = x.MachineName,
    .ServiceName = x.ServiceName
}).ToArray()

'UI thread
For Each x In serviceControllers.Zip(DataGridView1.Rows.Cast(Of DataGridViewRow), Function (sc, dgvrow) New With { sc, dgvrow })
    x.dgvrow.Cells(2).Value = x.sc.Status.ToString
Next

如果行在计算过程中更改顺序,请使用此选项。

'UI thread
Dim inputs = DataGridView1.Rows.Cast(Of DataGridViewRow).Select(Function(dgvrow) New With _
{
    .MachineName = dgvrow.Cells(0).Value,
    .ServiceName = dgvrow.Cells(1).Value,
    .dgvrow = dgvrow
}).ToArray()

'Parallel
Dim results = inputs.AsParallel().Select(Function (x) New With
{
    .sc = New ServiceController With _
    {
        .MachineName = x.MachineName,
        .ServiceName = x.ServiceName
    }, _
    .dgvrow = x.dgvrow
}).ToArray()

'UI thread
For Each x In results
    x.dgvrow.Cells(2).Value = x.sc.Status.ToString
Next

请记住,如果在计算过程中删除行,这也会失败。


您应该使用Microsoft的Reactive Framework(又名Rx)-NuGet System.Reactive.Windows.Forms并添加using System.Reactive.Linq;-然后您可以执行以下操作:

'UI thread
Dim inputs = DataGridView1.Rows.Cast(Of DataGridViewRow).Select(Function(dgvrow) New With _
{
    .MachineName = CType(dgvrow.Cells(0).Value, String),
    .ServiceName = CType(dgvrow.Cells(1).Value, String),
    .Row = dgvrow
}).ToArray()

'Rx query
Dim query = _
    From x In inputs.ToObservable()
    From s In Observable.Start(Function () FetchStatus(x.MachineName, x.ServiceName))
    Select New With
    {
        x.Row,
        .Status = s
    }

'Rx subscription
Dim subscription As IDisposable = _
    query _
        .ObserveOn(DataGridView1) _
        .Subscribe(Sub (x) x.Row.Cells(2).Value = x.Status)

这将在响应返回时尽快更新每一行-并将并行处理。

我假设您具有一个带有以下签名的函数:Function FetchStatus(MachineName As String, ServiceName As String) As String