インスタンス コンストラクターは静的メンバーを設定しますが、スレッド セーフですか?
-
09-06-2019 - |
質問
いくつかのコードをリファクタリングしていますが、 lock
インスタンスコンストラクター内で。
public class MyClass {
private static Int32 counter = 0;
private Int32 myCount;
public MyClass() {
lock(this) {
counter++;
myCount = counter;
}
}
}
ご確認ください
- インスタンス コンストラクターはスレッドセーフです。
- lock ステートメントは、静的な「counter」メンバーではなく、そのコード ブロックへのアクセスを防止します。
元のプログラマーの意図が、各インスタンスにその「カウント」を認識させることだった場合、別のスレッドが新しいスレッドを作成していないことを確認するには、「カウンター」メンバーへのアクセスをどのように同期すればよいでしょうか。 MyClass
これがカウントを設定する前にカウントを変更しますか?
参考 - このクラスはシングルトンではありません。インスタンスは単にその数を認識する必要があります。
解決
@ajmastrean
シングルトン パターン自体を使用するべきだと言っているわけではありませんが、インスタンス化プロセスをカプセル化するその方法を採用してください。
つまり
- コンストラクターをプライベートにします。
- 型を返す静的インスタンス メソッドを作成します。
- 静的インスタンス メソッドでは、インスタンス化する前に lock キーワードを使用します。
- 型の新しいインスタンスをインスタンス化します。
- カウントをインクリメントします。
- 新しいインスタンスのロックを解除して返します。
編集
私に起こった問題の 1 つは、カウントがいつ減少したかをどうやって知ることができるでしょうか?;)
再編集
考えてみると、別の静的メソッドを呼び出すコードをデストラクターに追加してカウンターをデクリメントすることができます:D
他のヒント
数値をインクリメントするだけの場合は、そのための特別なクラス (Interlocked) があります。
http://msdn.microsoft.com/en-us/library/system.threading.interlocked.increment.aspx
Interlocked.Increment メソッド
指定された変数をインクリメントし、その結果をアトミック操作として保存します。
System.Threading.Interlocked.Increment(myField);
スレッドのベスト プラクティスに関する詳細...
これはシングルトンパターンかそれに類するものだと思います。あなたがやりたいのは、オブジェクトをロックすることではなく、変更中にカウンターをロックすることです。
private static int counter = 0;
private static object counterLock = new Object();
lock(counterLock) {
counter++;
myCounter = counter;
}
現在のコードはある意味冗長だからです。特にコンストラクター内では、コンストラクターを呼び出すことができるスレッドが 1 つしかありません。これは、スレッド間で共有され、共有されている任意のスレッドからアクセスできるメソッドとは異なります。
あなたのコードからわかることは、オブジェクトの作成時の現在のカウントをオブジェクトに与えようとしているということです。したがって、上記のコードでは、カウンターがローカルで更新および設定されている間、カウンターはロックされます。したがって、他のすべてのコンストラクターはカウンターが解放されるまで待つ必要があります。
別の静的オブジェクトを使用してロックすることができます。
private static Object lockObj = new Object();
そしてこのオブジェクトをコンストラクターでロックします。
lock(lockObj){}
ただし、コンパイラの最適化によって処理する必要がある状況があるかどうかはわかりません。 .NET
Javaの場合のように
これを行う最も効率的な方法は、インターロックされたインクリメント操作を使用することです。カウンタをインクリメントし、静的カウンタの新しく設定された値を一度に (アトミックに) 返します。
class MyClass {
static int _LastInstanceId = 0;
private readonly int instanceId;
public MyClass() {
this.instanceId = Interlocked.Increment(ref _LastInstanceId);
}
}
元の例では、個々のインスタンスには異なる「this」参照があり、複数のインスタンスが同時に静的メンバーを更新する可能性があるため、lock(this) ステートメントは望ましい効果を持ちません。
コンストラクターは、コンストラクターが完了するまで構築中のオブジェクトへの参照が表示されないため、ある意味ではスレッド セーフであると考えることができますが、これは静的変数の保護には役に立ちません。
(マイク・シャルが最初に連動ビットを持っていました)
変更すると思います シングルトンパターン カウントを含めるには(明らかにスレッドセーフな方法を使用します)、問題ありません:)
編集
うっかり削除してしまったクズ!
インスタンスコンストラクターかどうかはわかりません は スレッド セーフです。デザイン パターンの本でこれについて読んだ記憶があります。純粋にこのため、インスタンス化プロセス中にロックが確実に設定されるようにする必要があります。
@ロブ
参考までに、このクラスはシングルトンではない可能性があります。別のインスタンスにアクセスする必要があります。単純にカウントを維持する必要があります。「カウンター」インクリメントを実行するには、シングルトン パターンのどの部分を変更しますか?
それとも、ロックを使用してカウンターをインクリメントして読み取るコードへのアクセスをブロックする構築用の静的メソッドを公開することを提案していますか。
public MyClass {
private static Int32 counter = 0;
public static MyClass GetAnInstance() {
lock(MyClass) {
counter++;
return new MyClass();
}
}
private Int32 myCount;
private MyClass() {
myCount = counter;
}
}