質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

2回答

1382閲覧

共有オブジェクトの設計

bell_21

総合スコア1

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2021/06/24 13:31

編集2021/06/26 07:42

質問

複数スレッドから読み書きを行う、共有オブジェクトの作成に悩んでいます。
オブジェクトは通信している一つ機器の状態を保持しています。

###条件
一つの機器の状態ですが、すべての状態において読み書きが同時にできないようしなくても
項目ごとに読み書きができなくなれば良いかなと考えております。
可読性が低くなりそうなら全体としても大丈夫です。

例として下記のようなDataManageクラスを使用しますが、
実際にはDataManageクラス内にその他情報もある想定です。
(DataManageは機器、State1/State2は機器の状態を項目分けしたものになります。)
項目を内部クラスとしていますが外でも大丈夫です。

C#

1  1、プロパティで公開せず関数による項目ごとのアクセスにする。 2 public class DataManage 3 { 4 public class State1 5 { 6 public int A { get; set; } 7 public int B { get; set; } 8 } 9 10 public class State2 11 { 12 public int C { get; set; } 13 public int D { get; set; } 14 } 15 16 private object syncState1 = new object(); 17 private object syncState2 = new object(); 18 private State1 state1 = new State1(); 19 private State2 state2 = new State2(); 20 21 public State1 GetState1() 22 { 23 var ret = new State1(); 24 lock (syncState1) 25 { 26 // ここでコピーなど 27 } 28 return ret; 29 } 30 public State2 GetState2() 31 { 32 var ret = new State2(); 33 lock (syncState2) 34 { 35 // ここでコピーなど 36 } 37 return ret; 38 } 39 public void SetState1(State1 state) 40 { 41 // ここでコピーなど 42 } 43 public void SetState2(State2 state) 44 { 45 // ここでコピーなど 46 } 47 /// <summary> 48 /// 必要によって個々の状態にアクセスできるように。。。 49 /// </summary> 50 public int GetState1A() 51 { 52 lock (syncState1) return state1.A; 53 } 54 public void SetState1A( int val ) 55 { 56 lock (syncState1) state1.A = val; 57 } 58 } 59 60  2、状態をさらにラップするクラスを設けプロパティでアクセスできるようにする。 61 public class DataManage 62 { 63 64 public class State1Lap 65 { 66 public class State1 67 { 68 public int A { get; set; } 69 public int B { get; set; } 70 } 71 72 private State1 state = new State1(); 73 private object syncState = new object(); 74 75 public int A 76 { 77 get { lock (syncState) return state.A; } 78 set { lock (syncState) state.A = value; } 79 } 80 public int B 81 { 82 get { lock (syncState) return state.B; } 83 set { lock (syncState) state.B = value; } 84 } 85 86 public State1 GetState() 87 { 88 var ret = new State1(); 89 lock (syncState) 90 { 91 // ここでこぴーなど 92 } 93 return ret; 94 } 95 96 public void SetState(State1 state) 97 { 98 // ここでこぴーなど 99 } 100 101 } 102 103 public class State2Lap 104 { 105 public class State2 106 { 107 public int C { get; set; } 108 public int D { get; set; } 109 } 110 111 private State2 state = new State2(); 112 private object syncState = new object(); 113 114 public int C 115 { 116 get { lock (syncState) return state.C; } 117 set { lock (syncState) state.C = value; } 118 } 119 public int D 120 { 121 get { lock (syncState) return state.D; } 122 set { lock (syncState) state.D = value; } 123 } 124 125 public State2 GetState() 126 { 127 var ret = new State2(); 128 lock (syncState) 129 { 130 // ここでこぴーなど 131 } 132 return ret; 133 } 134 135 public void SetState( State2 state ) 136 { 137 // ここでこぴーなど 138 } 139 } 140 private State1Lap state1 = new State1Lap(); 141 private State2Lap state2 = new State2Lap(); 142 143 public State1Lap State1 => state1; 144 public State2Lap State2 => state2; 145 } 146 147  3、そもそも項目ごとに分けずすべてを並列に管理する。 148 public class DataManage 149 { 150 private object syncState = new object(); 151 private int a { get; set; } 152 private int b { get; set; } 153 154 private int c { get; set; } 155 private int d { get; set; } 156 157 public int A 158 { 159 get { lock (syncState) return a; } 160 set { lock (syncState) a = value; } 161 } 162 public int B 163 { 164 get { lock (syncState) return b; } 165 set { lock (syncState) b = value; } 166 } 167 168 public int C 169 { 170 get { lock (syncState) return c; } 171 set { lock (syncState) c = value; } 172 } 173 public int D 174 { 175 get { lock (syncState) return d; } 176 set { lock (syncState) d = value; } 177 } 178 179 public DataManage Clone() 180 { 181 var datamanage = new DataManage(); 182 lock (syncState) 183 { 184 // コピーなど 185 } 186 return datamanage; 187 } 188 } 189 190  4、そもそもの項目オブジェクトを保証できるようにしておく 191 public class DataManage 192 { 193 public class State1 194 { 195 private object syncState = new object(); 196 private int a { get; set; } 197 private int b { get; set; } 198 199 public int A 200 { 201 get { lock (syncState) return a; } 202 set { lock (syncState) a = value; } 203 } 204 public int B 205 { 206 get { lock (syncState) return b; } 207 set { lock (syncState) b = value; } 208 } 209 210 public State1 Clone() 211 { 212 var ret = new State1(); 213 lock (syncState) 214 { 215 // ここでこぴーなど 216 } 217 return ret; 218 } 219 220 public void Copy(State1 state) 221 { 222 // ここでこぴーなど 223 } 224 } 225 226 public class State2 227 { 228 private object syncState = new object(); 229 private int c { get; set; } 230 private int d { get; set; } 231 232 public int C 233 { 234 get { lock (syncState) return c; } 235 set { lock (syncState) c = value; } 236 } 237 public int D 238 { 239 get { lock (syncState) return d; } 240 set { lock (syncState) d = value; } 241 } 242 243 public State2 Clone() 244 { 245 var ret = new State2(); 246 lock (syncState) 247 { 248 // ここでこぴーなど 249 } 250 return ret; 251 } 252 253 public void Copy( State2 state ) 254 { 255 // ここでこぴーなど 256 } 257 } 258 private State1 state1 = new State1(); 259 private State2 state2 = new State2(); 260 261 public State1 State1 => state1; 262 public State2 State2 => state2; 263 }

考え

1、値の保証はできるが、プロパティとして個々の変数にアクセスすることができず、
個々の値が欲しいのに、状態全体を取得しないといけない。

2、一番ましなように思えます。

3、保証はされているが、State1/State2としてかたまりで取得できない。

4、値を保証するといった観点では目標を達成しているが、オブジェクトをDataManageからローカルにコピーして使用する場合一つのスレッドからしか読み書きが行われないので、保証する必要がないのでなしかなと。

### 聞きたいこと
複数のスレッドから書き込み/読み込みが行われる想定の値を保持したクラスをどのように作成したらよいかです。

他にもいい方法や指摘などがあればよろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/06/25 02:54 編集

単なるアンケートになっていて、どういう問題があって、どうなれば解決かが示されていないので回答出来ないです。特に大きな問題がなければ、とりあえず一番ましと思われる方法でやってみればよいのではないでしょうか? Interlocked クラスというものもあるので、一応紹介しておきます。 https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.interlocked
neconekocat

2021/06/25 08:48

提示されてるクラスが色々と問題あってどう答えればいいか分からないんですよね 1はそもそもロックが意味をなしていないしSetStateはインスタンスの上書きでなくディープコピーをやりたがっているように見える あくまでサンプルを見ただけで言えば3が一番マトモに見えますよ
bell_21

2021/06/26 07:39

radianさん 指摘ありがとうございます。確かにアンケートになっていますね、、 解決方法というよりは、どのようにクラスの設計をしたら良いのかを聞きたかった次第です。
bell_21

2021/06/26 07:44

neconekocatさん、ありがとうございます。 経験が浅いので問題が自分には上げられないのですが、よろしければ教えていただけないでしょうか。 1に関しては確かにディープコピーをする想定でした。 設定するほうにもロックが必要な感じですかね、、
neconekocat

2021/06/26 08:38

そもそもロックはどこからどこまで必要かを勘違いしているような気がします。 ロックする範囲は「値を取得(設定する)必要がある処理の開始時から終了時まで」です。 例えば、通信機器が通信中かどうかの状態が格納されているとします。 ここで、「通信中ではないならば通信を開始し、状態を通信中にする」という処理を実行する場合、最初から最後までロックを続けなければいけません。値を確認した後にロックを解除した場合、通信開始前に他スレッドが値を書き換えてしまう可能性がありますからね。
bell_21

2021/06/27 04:21

確かにロックをかける範囲の認識が間違っていと思います。 「読み込んでから状態によって書き込む」みたいな動作を複数のスレッドが行う場合、セマフォやミューテックス(ロックならロックするオブジェクトを読み取り専用で公開?)みたいにするのでしょうか。
neconekocat

2021/06/27 04:46 編集

既に回答者が出ていますのでこれで終わりますが、要件によるとしか言えません。 その例の場合であればプロパティ毎にロック用のものを個別に用意します。 またState1のようにデータを構造体として纏めたい場合はイミュータブルなクラスにし、値書き換え=別インスタンスをセットするようにします。構造体として纏める意味がないなら個別のプロパティとして実装するほうが設計としてはキレイです。(3が一番マトモに見えると書いたのはそれが理由です)
退会済みユーザー

退会済みユーザー

2021/06/30 02:30 編集

この質問は何をもってクローズするつもりなのでしょう? 再設計するという結論になったのであれば、質問内容自体がほぼ無意味なものになりますので、一旦クローズし、再設計後に具体的な問題が出てきたら新たに質問してください。 明確な答えがない質問をダラダラと引き延ばすつもりなら低評価せざるを得ません。
bell_21

2021/07/03 15:06

すみません、クローズしわすれました。 仕様を再確認し疑問があれば再度ここで質問しようと思います。
guest

回答2

0

あくまで例ですがlock用のobjectを用意したい場合だとそれっぽいクラスでラップしたりします。

C#

1public class LockManager<T> 2{ 3 public object LockObject { get; } = new object(); 4 public T Value { get; set; } = default(T); 5} 6 7public class DataManage 8{ 9 public LockManager<int> A = new LockManager<int>(); 10 public LockManager<int> B = new LockManager<int>(); 11} 12 13private static readonly DataManage m_Data = new DataManage(); 14 15static void Test() 16{ 17 // 値の更新 18 lock (m_Data.A.LockObject) 19 m_Data.A.Value = 100; 20 21 // 値の参照 22 lock (m_Data.A.LockObject) 23 Console.WriteLine($"A = {m_Data.A.Value}); 24}

ただしロック対象を細分化すると、当然デッドロックさせてしまう可能性も高まります。

C#

1static void deadlock() 2{ 3 Task.Run(() => 4 { 5 lock(m_Data.A.LockObject) 6 { 7 Thread.Sleep(200); 8 lock (m_Data.B.LockObject) // ←ここと 9 Console.WriteLine($"A = {m_Data.A.Value}, B = {m_Data.B.Value}"); 10 } 11 }); 12 13 Thread.Sleep(100); 14 15 lock (m_Data.B.LockObject) 16 lock (m_Data.A.LockObject) // ←ここで死ぬ 17 Console.WriteLine($"A = {m_Data.A.Value}, B = {m_Data.B.Value}"); 18}

・同時にアクセスする可能性がある変数は1つのクラスにしてクラス単位でアクセスする
ReaderWriterLockSlimクラスのようにデッドロックを防ぎやすいクラスを使う
等、考慮すべきことが非常に多いため一概にコレがいいというやり方はありません。
まずは要件を分析し、必要な仕様の洗い出しをしてからクラス設計を考えて下さい。

投稿2021/06/27 08:39

neconekocat

総合スコア443

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bell_21

2021/06/29 12:14

まとめた構造にしたい場合はクラス単位でのアクセスにしたほうが確かにいいかもしれないです。 ReaderWriterLockSlimについては、少し調べさせていただきます。 やはり、あいまいな状態で設計を行うのがまず間違いですね。。
guest

0

ベストアンサー

こういう質問が出てくる時点で、ロックによって何を実現したいか・保証したいかが仕様として明確になっていないのではないですか?
例えば、1でGetState1する時、State1をコピーして返す感じに見えますが、
【GetState1呼び出し直後のState1】:A=100 B=200
【コピー中に別スレッドからB=300に書き換えが発生】:A=100 B=300
【実際に返されたState1】A=100 B=300
という結果が戻ってきて問題ないのかどうか、という事です。GetState1呼び出し直後のState1の状態を保証したいのであれば、その間にState1に書き込めたらまずいから、コピーが終わるまで書き込みをロックしないといけないですよね。
単純なint値の読み書きだけなら、そもそもロック不要な事が多いです。
何故ロックする必要があるのか、いつからいつまでロックしないといけないのか、を仕様として整理し直せば自ずとクラス設計も決まってくるでしょう。

投稿2021/06/26 09:57

編集2021/06/26 10:01
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bell_21

2021/06/27 04:37

確かにご指摘の通り、仕様があやふやな状態でプログラムを作ろうとしていました、、 仕様を整理するのが先ですね。 すみませんが、1のコードですが、SetState1にも同様なロックが抜けておりました。 普通という聞き方はよくはないと思いますが、普通データの操作中をロックする場合、データとは関係ないところでロック用のオブジェクトを定義/参照して行う(Lock使用)。または、データ内部に設定変更中(ManualResetEvent等)などを設け行う。 などがある?かと思いますがどのようにするのでしょうか。 教えていただけると幸いです。
退会済みユーザー

退会済みユーザー

2021/06/27 05:44 編集

ManualResetEventは複数のスレッド間で、実行タイミングの調整に使うものです。単純な排他用途なら普通にlockでいいです。同期オブジェクトに関する記事は、検索したら詳しい記事がいくらでも出てくると思うので自分で調べてみてください。
bell_21

2021/06/29 12:07

ありがとうございます。 知識不足を再認識しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問