|
■No103551 (ゆーすけ さん) に返信 > 本題とは直接関係ない部分かなと判断し省略してしまいました 全部書いても丸投げになってしまうので、判断は難しいですよね。 「現象を再現可能な最低限のコード」にまで絞り込めれば、問題点をより具体的に指摘できる可能性もありますが。
個々のデータはインデックスで識別されるようなので、自分なら UserParameter の各メンバーは ReadOnly にすると思いますし、 SimulationResult においても、少なくとも idx は ReadOnly にします。今回の問題には無関係な話ですけれどね。
配列で管理されている SimulationResult は、List を内包しているみたいですし、Structure ではなく Class にするかな…。 その最終結果を蓄積させる simPartResult も、配列にはせず、List(Of SimulationResult) もしくは Dictionary(Of Integer, SimulationResult) で管理するかも知れません。まぁその辺は好みの問題。
閑話休題。
> 私の使い方だとクラスにする必要はなくモジュールとして記述すべきということでしょうか? 自分が Module を使うのは拡張メソッドを作る時ぐらいなので、個人的には「否」です。
元コードを見ていないため断言はできませんが、今回はフィールドが無いということですし、 Shared のままで良いと思いますよ。 単一のクラスで済ませるべきか、複数のクラスにわけるべきかは見てみないと分かりません。
引数と戻り値だけですべてが語れるメソッドであるならば、大抵は共有メソッドで済ませています。 ただし状態値を持ったオブジェクトとしておきたい場合、あえてインスタンス化できるクラスとして設計しておき、 そのインスタンスを DoWork 内で生成するようにする設計とすることもあります。その場合、 Singleton-class にすることもあったりして……まぁケースバイケースですね。
インスタンス不要な場合、 C# であれば静的クラス(static class)を採用したいところですが、 VB には Shared Class というものが無いので、せめてインスタンス化だけ封じています。
Partial Public NotInheritable Class CM Private Sub New() End Sub End Class
機能的には Module でも良いとは思いますが、Module だと名前空間を明示せずにアクセスできてしまうことから、 個人的にはあまり好みません。「共通して使うもの」だからこそ、Namespace で明確に管理できた方が、 複数のプロジェクトから使う際にも、複数の共通ライブラリを併用しやすいと思っているもので。
> ちなみにCMクラス中にはフィールド変数(要はCMクラスの中で各プロシージャの外で宣言されている変数ですよね?)はありませんが、 > SolverMain中で非常に高頻度で使う物理定数はCMの先頭においてPublic Constでフィールド定数(?)として宣言しています。 > これは関係あったりするでしょうか?(作業中に変化しないので無関係?) Constを使うことは問題ありません。名前付きリテラル扱いであり、値が変化することは無いですから。
一方、ReadOnly Field や ReadOnly Property だと、値を変更できる可能性が一応あります。
Class Compiler Shared ReadOnly Months() As Integer = {31,28,31,30,31,30,31,31,30,31,30,31} Shared Function Main() As Integer Console.WriteLine(Months(1)) Months(1) = 29 Console.WriteLine(Months(1)) Return 0 End Function End Class
> またSoverMain中のメソッドがスレッドセーフであることの確認ですが、これは私がCMクラス内に定義したSolverMainやその中で使用される > ユーザー定義関数だけではなく、それらの関数中で使用されている演算も含めて、という理解でよろしいでしょうか? 「他のスレッドから同時にアクセスされる変数が無い」のならばスレッドセーフです。 しかし「複数のスレッドで共有されている変数」があるのなら、演算部も含めて それらがマルチスレッド対応であるかどうかを、常に意識してコーディングせねばなりません。
たとえば、「64bit 整数型(Long あるいは ULong)を読み取る」というだけの単純処理においても、 32bit CPU 環境では、上位32bitと下位32bitの読み取りが命令が発生するため、 「読み取り開始から読み取り完了までの間に、他のスレッドによって編集されていた」という可能性が起こりえるため、 読み取り処理を不可分とするために、わざわざ Interlocked.Read メソッドという専用命令が利用されています。 もちろん他のスレッドから同時にアクセスされることのない変数に対しては不要ですけれどね。
> (たとえばA = A + B のような通常の加算処理など) 変数 A と変数 B の両方がローカル変数な Integer 型であるのならば問題もありません。
しかし、それらの変数のいずれかを ByRef 引数のメソッドに渡していた場合には、 スレッドセーフではなくなる可能性がありえます。 (そのメソッド内で別スレッドが生成されており、引数の内容を書き換えてしまう場合など)
> CMクラス、またはモジュール中に記述されたSolverMainその他のFunctionプロシージャがプロシージャ内で宣言されたローカル変数か > 引数としてプロシージャに渡された変数しか使っていない場合は、SolverMainが複数のスレッドから呼び出されても問題はないのでしょうか?
SolverMain がローカル変数しか使っていなかったとしても、その中で「他のメソッド」を呼び出しているなら、 それぞれのメソッドがスレッドセーフであるかどうかを確認する必要があるでしょう。
引数として渡された値も、たとえば BackgroundWorker1.RunWorkerAsync(arg(0)) BackgroundWorker2.RunWorkerAsync(arg(1)) みたいな処理をしている場合、それぞれの処理が終わる前に、 呼び出し元から arg(0) や arg(1) を読み書きしてはいけないのは言わずもがな。
そういえば現状の実装では、「すべてのスレッドの処理が完了したのかどうか」を どうやって判断しているのでしょうか。
> とするとどのように確認をすればいいかご教示いただければと思います。 値が「どこで書き込まれているか」「どこで読み取っているか」をきちんと把握した上で、 それらが「それらは複数のスレッドで同時にアクセスされうるのかどうか」を考えてコーディングします。
そのうえで、「そもそも同時に読み書きさせない」設計にするか、あるいは 「並列で読み書きできるよう排他制御を組み込む」ようにする、ということです。
自作処理であれば、同時アクセスの禁止については開発者自身が責任を持つしかありません。 既存のライブラリなら、ドキュメントの記載で判断するか、その型のソースコードを読み解くかです。
たとえば「配列」(System.Array)クラスのドキュメントに、「スレッド セーフ」という項がありますね。 https://learn.microsoft.com/ja-jp/dotnet/api/system.array?WT.mc_id=DT-MVP-8907&view=netframework-4.8.1#thread-safety >> >> この型のパブリック静的 (Visual Basic のShared) メンバーはスレッド セーフです。 >> インスタンス メンバーがスレッド セーフであるとは限りません。 >> >> この実装では、Arrayの同期 (スレッド セーフ) ラッパーは提供されません。ただし、Array に基づく .NET クラスは、 >> SyncRoot プロパティを使用して独自の同期バージョンのコレクションを提供します。 >> >> コレクションを列挙することは、本質的にスレッド セーフなプロシージャではありません。 >> コレクションが同期されている場合でも、他のスレッドはコレクションを変更できるため、列挙子は例外をスローします。 >> 列挙中のスレッド セーフを保証するには、列挙全体の間にコレクションをロックするか、 >> 他のスレッドによって行われた変更によって発生する例外をキャッチします。 >>
■No103552 (ゆーすけ さん) に返信 > Public Structure VECTOR_D > Dim x As Double > Dim y As Double > Dim z As Double > End Structure
その構造体が、それぞれのスレッドで個別管理される値(他のスレッドと共有されていないもの)ならば、 先に述べた通り、特に気にする必要はありません。
しかし、複数のスレッド(メインスレッドと作業スレッドなど)で共有されていた場合については、 「その構造体が同時にアクセスされるのかどうか」を常に考慮せねばなりません。 例に挙げられたインクリメントのように、「読み取りも書き込みも行われる」変数であれば、 SyncLock ステートメントなどを使って排他処理が必要になることもあろうかと思います。
あるいは 3元ベクトルという事は xyzの3値が揃って初めて意味を持つものであることから、 「演算によって 3値を書き換えている最中」に、別の処理で「ベクトル値を読み取る」ことが 無いようにしたい…という要件が必要になることもありえるかと。(マルチスレッドかどうかとは無関係に)
例として、x=0,y=0,z=0 が演算によって x=2,y=3,z=-1 に変わる処理があったとします。 現在の VECTOR_D の構造だと、「0,0,0」から「2,3,-1」へと変更している最中に、 その変数が読み取られると、編集途中の「2,3,0」という値が読み取られてしまう可能性がありえます。
そういったことを避けるため、複数メンバーを持つクラスや構造体においては、 『一部だけが書き換わった状態』で読み取られることを防ぐ設計にしておくこともあります。 たとえば、変更不能な immutable 型として実装するなど。
具体的には、複数メンバーの値をコンストラクタで一斉に指定できるようにし、 それぞれのメンバーは Public ReadOnly Property にしておいて、 演算については Shared メソッドや演算子オーバーロードを用いて、 演算結果を Return するという実装にする形というものです。
このパターンの実装例としては、複素数型 (System.Numerics.Complex 構造体)などがあります。 https://learn.microsoft.com/ja-jp/dotnet/api/system.numerics.complex?WT.mc_id=DT-MVP-8907&view=net-8.0
Complex 構造体のソースコードはこちら。 [.NET Framework 版] https://referencesource.microsoft.com/#q=System.Numerics.Complex [.NET 版] https://source.dot.net/#q=System.Numerics.Complex
|