web-dev-qa-db-ja.com

静的変数-使用法とスレッド化への影響

クラスでの静的変数/参照の使用に関して、いくつか混乱があります。何かを静的に保つことの意味を完全に理解していないように思えます。

変数がpublic staticであると言うとき、それはクラスごとの変数の1つのインスタンスですか、アセンブリごとの1つのインスタンスですか?

たとえば、ユーザーコントロールがあります(たとえば、WPFアプリの場合)。このユーザーコントロールには、コントロール内のさまざまなものを調整する静的なパブリック変数があります。このユーザーコントロールを使用するフォームがあり、フォームごとにこのコントロールのインスタンスが複数ある場合、パブリック静的変数のインスタンスがいくつ作成されますか? (私の推測は1つだけであり、すべてのコントロールによって変更されます)。

今、私は実際には、コントロールの各インスタンスにそのような変数を1つ持たせる必要があります。単一のフォーム内のコントロールのすべてのインスタンスにわたる1つの変数ではありません。

「lock」キーワードを使用したシングルトンパターンを使用すると、この問題は解決しますか?つまり、ユーザーコントロールのインスタンスごとに1つの変数が作成されますか?そうでない場合、シングルトンパターンはどの問題を解決しますか?この設計問題を解決するためのオプションは何ですか?

(注:静的ルートとその正常な動作を行う代わりに、内部の非静的グローバル変数を作成して慎重に渡すことに頼りました。別のオプションがあるかどうかを確認しています。)

(質問が非常にナイーブまたは未調査のように聞こえる場合、私と一緒にください。私は投稿する前に少し読んでいましたが、それを私に非常に明確に説明する答えを見つけることに行き詰まっているようです。)

6
Harsha

コントロールで何を達成しようとしているのか、またはその理由を推測するという誘惑を完全に無視します。基本的な質問への回答を簡単に試してみましょう。

前もって;あなたの説明から、あなたの最良の選択肢は、静的ではなくシングルトンではなく、インスタンスフィールドを持つことだと思います。 「コントロールの各インスタンスには、そのような変数が1つ必要です。その単一のフォーム内のコントロールのすべてのインスタンスにまたがる1つの変数ではありません。」これは、インスタンス変数の緩い定義のようなものです。本当に公開する必要があるのでしょうか?

実際、私はそれをインスタンスpropertyにします。なぜなら、物事が変化したときに遅かれ早かれ何らかのイベントを発生させる可能性がかなり高いからです。または、パブリックフィールドを振り返ってみると不幸な決断のように感じることになる他のことをします。 Hindsightは常に20/20です。または少なくとも20/30かそこら。しかし、この場合、少しforesightは、単純な自動プロパティを使用すると安価な保険になると述べています。

public SomeDataType WidgetSettings{ get; set; }

そう; アセンブリ全体の静的フィールド/変数のインスタンスは1つしかありません。そのパブリック静的変数を含むクラスのすべてのインスタンスは、メモリ内のまったく同じオブジェクトへの参照を持っています。つまり、すべてのコントロールインスタンスがまったく同じオブジェクト/メモリにアクセスしています。

「static」と「public」が同じ変数宣言行に一緒に表示されるときはいつでも、立ち止まって考える必要があります。問題は、変数が文字列、数値型、または単純なコレクションのようなものである場合、そのフィールドにアクセスする同期コードeveryplaceを使用する必要があるということです。一方、さまざまな属性の静的ゲッターとセッターを持つクラスへの参照の場合は、その同期コードをゲッターとセッターに配置し、そのクラスとその属性を(ユニットとして)分離してテストできます。そしてそれが適切に機能することを知っています。私が意図したことと同じようにそれを表現したかどうかはわかりませんが、正直なところ、インスタンス変数を使用してスレッドセーフの問題全体を回避するために必要なことができる場合は、代わりにそれを行います。

静的変数がある場合、それは確実にスレッドセーフではありません。全然。 Winformsアプリを実行していて、すべてのUI要素が単一のスレッドで実行されていると考えている可能性があります(Winformsはスレッドセーフではないため)。タイマーやバックグラウンドワーカーなどを使用して物事を緩めることについて創造的です。したがって、奇妙なことが起こる可能性はまだあります。したがって、最悪の場合、すべてのコントロールインスタンスが同じ変数にアクセスして更新するだけでなく、プロセス内で互いに踏みにじられる可能性があります。

WPFをいじっている場合、UIライブラリisスレッドセーフであり、静的フィールドと組み合わせたマルチスレッドの潜在的な落とし穴が適用されます。

変数が指すデータが、完全に書き込むために複数の命令を必要とするタイプである場合、次のコントロールインスタンスがそのフィールドを読み取って取得する前に、1つのコントロールインスタンスが新しい値の半分のみを書き込む前に、それは時間の問題です。 1つの書き込みの半分と別の書き込みの半分で構成される値。その状況にふさわしい専門用語は「フバー」に沿ったものだと思います。もちろん、そのマングルされた変数を読み取るスレッドは、スレッドスケジューラによってスワップアウトされて元のスレッドが再度実行される前に、変数に書き戻そうとします。最初のスレッドの半値を、最初のスレッドの前の独自の半値に置き換えます。スレッドはits値の後半を書き込む機会さえ得ます。これは、涙、または脱毛症、またはより可能性の高い涙と脱毛症で終わる可能性があります。

最後に、シングルトンには、静的フィールドと同じ潜在的なスレッドセーフティの問題があります。シングルトンの全体的な概念は、ファクトリメソッドからインスタンスを取得することです。ファクトリメソッドは、要求するすべての人に同じ単一のインスタンスを提供します。したがって、質問する人は皆、定義上、まったく同じオブジェクトにアクセスします。

thisの場合、私の腸は、「singleton」が「fancy static field」のコードワードであることを教えてくれます。テストを行いますが、エラーが発生しやすく、パフォーマンスが低下する同期コードを記述しない限り、スレッドセーフではありません。」

コードワードの定義が長すぎる場合、別のアプローチを真剣に検討する価値があります。

それが価値があるもののために。 :-)詳細がなければ、質問に直接関連する回答を提供するのは非常に困難です。この静的フィールドはどのタイプのオブジェクトを指していますか?コレクションですか?それは、複数のプロパティを持つある種の設定または構成クラスですか?送信するさまざまな信号に応じてシステムの他の部分(イベント)と通信するオブジェクトですか?等。

3
Craig

あなたはこれをあなた自身のために複雑すぎてやっていると思います。ユーザーコントロールが参照するアプリケーションでは、ある種のプレゼンター/コントローラークラスを使用します。これにより、同じプレゼンターオブジェクトを使用している場合、すべてのユーザーコントロール間で状態が維持されます。

ここではシングルトンの必要はありません。シングルトンを使用する理由がより説得力のある理由がない限り、アプリケーション自体が各プレゼンター/コントローラーの状態を保持できます。


編集:

したがって、私が収集したコメントから、OPはコラボレーターオブジェクトがインスタンスごとに1つ存在することを望んでいます。つまり、インスタンスごとに1つのオブジェクトがあり、シングルトンの目的(または実際には静的なもの)ではありません。

次のようなコラボレーターを作成することもできます。

public class Collaborator {
    // members and methods
}

また、カスタムユーザーコントロールの場合は、コンストラクターで作成できますが、次のように基本クラスのコンストラクターを呼び出す必要もあります。

private Collaborator _collaborator;

// Constructor in your CustomUserControl
public CustomUserControl() : base() {
    _collaborator = new Collaborator;
}

コードが子ユーザーコントロールを作成しているときは常に、コンストラクターを介してコラボレーターに子ユーザーコントロールを渡すことができます。子ユーザーコントロールを作成するイベントがあるとします。

void OnButtonMakeSubClick(object sender, RoutedEventArgs e)
{
    _childControl = new ChildControl(_collaborator);
}

また、子コントロールに次のコンストラクタを追加します。

private Collaborator _collaborator;

public ChildControl(Collaborator collaborator) : base() {
    _collaborator = collaborator;
}

これがあなたが望むものを示唆しているといいのですが。

2
Spoike