分類:[VB.NET/VB2005 以降]
2023/04/08(Sat) 17:37:30 編集(投稿者)
2023/04/08(Sat) 17:36:31 編集(投稿者)
今更ながらVB.NETのBackgroundWorkerについての質問なのですが、同一の処理に対し異なる初期パラメータを与えて
それぞれの結果を得るプログラムをVB.NET(Visual Studio 2017)で作成しています。
個々のケースは互いに独立していて完全に並列化が可能なので高速化を図ろうと思い、BackgroundWorkerを同時に
複数個実行させて並列計算を行おうとしました(今更BackgroundWorkerなのはそれ以降の非同期/並列計算処理について
使い方を習得できていないためです)。
そこで以下のような形でプログラムを組んだのですが、BackgroundWorkerが1つしか実行されず困っています。
(コードは簡略化して要点のみ記してあります)
同一処理を行うBackgroundWorkerのインスタンスを指定した数だけ生成し、それぞれにパラメータを与えて実行させるものです。
なおメインの処理では別のクラスファイルに記述された非常に大がかりなFunctionプロシージャ(MainFunction)を用いて
処理を行っています(MainFunctionは引数を与えてから結果が返ってくるのに数秒〜数十秒かかる)。
Dim SimResult() As userDefinedSimResult ←結果を受け取るための構造体
Private Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim bg() As System.ComponentModel.BackgroundWorker
Dim NumParallel As Integer
Dim SimParam() As UserDefinedSimParameter ←BackgroundWorkerに引数を渡すための構造体
NumParallell = ●● ←ユーザー入力により並列数を指定
ReDim bg(NumParallel - 1)
ReDim SimParam(bg.Getupperbound(0)), SimResult(bg.Getupperbound(0))
For i As Integer = 0 To bg.Getupperbound(0)
'=====この間で=====
SimParam(i) = 〜〜
SimParam(i).idx_bg = i
'=====SimParam(i)の値を決定=====
bg(i) = New As System.ComponentModel.BackgroundWorker
AddHandler bg(i).DoWork, AddressOf MainRoutine_DoWork
AddHandler bg(i).ProgressChanged, AddressOf MainRoutine_ProgressChanged
AddHandler bg(i).RunWorkerCompleted, AddressOf mainRoutine_RunWorkerCompleted
bg(i).RunWorkerAsync(SimParam(i))
Next
End Sub
Private Sub MainRoutine_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
Dim SimParamInBg As UserDefinedSimParameter
Dim SimResultInBg As UserDefinedSimResult
SimParamInBg = DirectCast(e.Argument, UserDefinedSimParameter)
SimResultInBg = CM.MainFunction(SimParamInBg) ←メインの計算処理(非常に重い)
e.Result = SimResultInBg
End Sub
Private Sub MainRoutine_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs)
Dim TempSimResult As UserDefinedSimResut
If(e.canceled = False) Then
TempSimResult = DirectCast(e.Result, UserDefinedSimResult)
SimResult(TempSimResult.idx_bg) = TempSimResult
End If
End Sub
以下、別のクラスファイルに記述されたメイン処理
Public shared Function MainFunction(Param As UserDefinedSimParameter) As UserDefinedSimResult
Dim Result As UserDefinedSimResult
'=====ここから非常に重い処理=====
Result = 〜〜
'=====ここまで非常に重い処理=====
Return Result
End Function
といった具合です。
実際に実行してみると複数生成したBackgroundWorkerのうち1つは動作していて値を返してくるのですが、その他はビジー状態に
なっているようでいくら待っても結果が返ってこず、NumParallel = 1にして並列計算を行わないようにすると正常に動作します。
試しにMainRoutine_DoWorkの中身を
Private Sub MainRoutine_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
Dim Test As Integer
Const CONSTANT_MAXIMUM As Integer = ●●
Do
Test = Test + 1
Loop Until(Test > CONSTANT_MAXIMUM)
e.Result = Test
End Sub
といった、他所の共通のFunctionプロシージャを呼び出さない別の処理にしてみたところ問題なく全てのBackgroundWorkerが実行されたので、
処理に時間のかかるFunctionプロシージャを複数のBackgroundWorkerから同時に使おうとして問題が起こっているのではと考えています。
(これまでも同様の実装で並列計算のプログラムを作ったことは何度かありましたが、その際にはDoWorkの中で共通のFunctionプロシージャといっても
一瞬で計算が終了する小さなものばかりでした)
もしこちらの想像通りだとすると、このような形での並列計算の実装は諦めるしかないのでしょうか?
それとも複数のBackgroundWorkerからMainFunctionを同時に使おうとすること自体は問題なく、何か他の原因で問題が起きているのでしょうか?
皆様のご教示をいただければと思います。