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

How to use Parallel.ForEach to loop into a DataGridViewRow

我在下面有一个代码,它循环遍历包含服务器和服务名称的数据网格视图中的每一行,并使用 ServiceController 引用来检查每个服务的状态和 return 单元格上的值。

        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,但我找不到合适的 code/combination 来完成这项工作。

我最初的尝试是

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>

A 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