Как использовать Parallel.ForEach для зацикливания в DataGridViewRow - PullRequest
0 голосов
/ 04 апреля 2019

У меня есть код ниже, который проходит по каждой строке в сетке данных, содержащей сервер и имя службы, и использует ссылку 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 голосов
/ 04 апреля 2019

A DataGridView является элементом пользовательского интерфейса - вам не разрешен доступ к нему из любого потока, кроме пользовательского интерфейса. Вы не можете использовать Parallel.ForEach на нем.

Что вы можете сделать, это извлечь данные как объект без пользовательского интерфейса, параллельно работать над этим, а затем присвоить результаты обратно.

Попробуйте это:

'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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...