下面有一个代码,它循环遍历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>
答案 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
。