等待多个后台工作程序完成

6

我需要处理多个大的二维数组(例如1024 x 128),在代码的某个部分,我需要转置其中的一些数组(最多12个)。 这个过程需要相当长的时间,我正在尽可能地加快速度。由于VB.NET支持多线程,我阅读了不同的来源,并在主子程序中编写了以下代码:

RunXTransposingThreads(Arr1, Arr2, Arr3, ...)

使用BackgroundWorker作为我的解决方案的一部分:

Private Sub RunXTransposingThreads(ParamArray ArraysToTranspose() As Array)
    Dim x = CInt(ArraysToTranspose.GetLength(0)) - 1
    Dim i As Integer
    For i = 0 To x
        Dim worker As New System.ComponentModel.BackgroundWorker
        AddHandler worker.DoWork, AddressOf RunOneThread
        AddHandler worker.RunWorkerCompleted, AddressOf HandleThreadCompletion
        worker.RunWorkerAsync(ArraysToTranspose(i))
    Next
End Sub

Private Sub RunOneThread(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
    Dim Transposed(,) As Single = Array.CreateInstance(GetType(Single), 0, 0) ' I need this to be like that in order to use other functions later
    Transposed = Transpose2dArray(CType(e.Argument, Single(,)))
    e.Result = Transposed
End Sub

Private Sub HandleThreadCompletion(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
    Debug.Print("Process complete")
End Sub

Function Transpose2dArray(Of Array)(ByVal inArray As Array(,)) As Array(,)

    Dim x = CInt(inArray.GetLength(1))
    Dim y = CInt(inArray.GetLength(0))
    Dim outArray(x - 1, y - 1) As Array

    For i = 0 To x - 1
        For j = 0 To y - 1
            outArray(i, j) = inArray(j, i)
        Next
    Next

    Transpose2dArray = outArray

End Function

线程似乎是有效的,因为在执行RunXTransposingThreads后的某个时候,我屏幕上会看到许多"Process complete"。问题是:如果我还没有转置数组,如何停止主代码的执行?

4
BGW已经过时,完全被Task.Runasync/awaitIProgress<T>所取代。使用Task.Run轻松启动大量任务,并用Task.WhenAll(arrayOfTasks)等待它们完成非常容易。另一方面,如果想要并行处理已经通过Parallel方法提供的大量数据,比如Parallel.ForEachParallel.For - Panagiotis Kanavos
我阅读了一些关于Async/Await和任务列表的文档,但似乎无法让任务返回一些结果。 - Noldor130884
@Noldor130884 啊!所以你的意思不是“停止执行主代码”,而是“等待后台工作完成”。最简单的方法是使用Panagiotis的建议,使用任务,否则如何正确等待BackgroundWorker完成?可以作为一个开始。 - Andrew Morton
@AndrewMorton 再次感谢。我会在程序运行后尽快尝试这些。 - Noldor130884
1
@PanagiotisKanavos,您能否写一个带有一点代码的答案,这样我就可以真正理解您使用AsParallel的意思了吗?C#代码也可以。 - Noldor130884
显示剩余5条评论
2个回答

2

正如其他人所说,BackgroundWorker已经过时。幸运的是,现在有许多其他现代化的方法可以实现这一点。

因此,我不会向您展示如何使您的代码与BackgroundWorker一起工作的内容。相反,我将向您展示如何使用任务(Tasks)来完成同样的事情,这是其中一种现代化的方式。希望这能帮到您。

Function RunXTransposingTasks(ParamArray ArraysToTranspose() As Array) As Array
    Dim taskList = New List(Of Task(Of Single(,))) ' our tasks returns Arrays

    For Each arr In ArraysToTranspose
        Dim r = arr
        taskList.Add(Task.Run(Function() Transpose2dArray(r)))
    Next

    Task.WhenAll(taskList) ' wait for all tasks to complete. 
    Return taskList.Select(Function(t) t.Result).ToArray()
End Function

Function Transpose2dArray(inArray As Array) As Single(,)
    Dim x = inArray.GetLength(1) - 1
    Dim y = inArray.GetLength(0) - 1
    Dim outArray(x, y) As Single

    For i = 0 To x
        For j = 0 To y
            outArray(i, j) = inArray(j, i)
        Next
    Next

    Return outArray
End Function

' Usage
' Dim result = RunXTransposingTasks(Arr1, Arr2, Arr3, ...)

1
避免使用 Task.StartNew,改用 Task.Run - Nkosi
为什么不将函数设为异步,并实际等待WhenAll调用完成,而不是调用阻塞的.Result调用呢? - Nkosi
@Nkosi 你又说对了。为什么不添加一个更好的答案呢? :) 我感觉我会从那个答案中学到很多东西。 - Kul-Tigin

-1
你可以尝试使用 内置函数 来复制数据。
Array.Copy(inArray, outArray, CInt(inArray.GetLength(1)) * CInt(inArray.GetLength(0)))

还有一些很好的示例,展示如何使用Parallel.ForEach。


好的,关于Array.Copy,谢谢你,但我不认为这个例子有帮助。我看到了很多关于如何在主函数中使用Parallel.ForEach的例子,但是我似乎找不到一个合适的例子来修改输入的数据... - Noldor130884

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接