ホームページ > バックエンド開発 > C#.Net チュートリアル > C#シングルトンパターンの実装と性能比較例

C#シングルトンパターンの実装と性能比較例

黄舟
リリース: 2018-05-16 17:08:52
オリジナル
2049 人が閲覧しました

この記事では、主に C# シングルトン モードの実装とパフォーマンスの比較に関する関連情報を紹介します。6 つの実装方法を詳しく紹介します。必要な方は参考にしてください。

単一のケースは、次のことができるクラスのみを指します。 1 つのインスタンスを持つ (C# では、より正確には、各 AppDomain に 1 つのインスタンスしか持てないクラス) は、ソフトウェア エンジニアリングで最も一般的に使用されるパターンの 1 つであり、最初のユーザーはこのクラスのインスタンスを作成した後、このクラスを使用する場合は、以前に作成されたインスタンスのみを使用でき、新しいインスタンスを作成することはできません。通常、C# でのシングルトン実装メソッドがいくつか導入されており、それらの間のスレッドの安全性とパフォーマンスの違いが異なります。シングルトンを実装するにはさまざまな方法がありますが、最も単純な実装 (非遅延ロード、非スレッド セーフ、低効率) から、遅延ロード、スレッド セーフ、効率的な実装まで、さまざまな方法があります。これらにはすべて、いくつかの基本的な共通点があります:

シングルトン クラスはすべて、引数のないプライベート コンストラクターを 1 つだけ持ちます

  • クラス宣言はシールされています (必須ではありません)

  • クラスには静的変数があります。作成されたインスタンスへの参照を保持します

  • シングルトン クラスは、作成されたインスタンスへの参照を返す静的メソッドまたはプロパティを提供します (例: GetInstance)

  • 複数の実装

1 つの非スレッド セーフティ

//Bad code! Do not use!
public sealed class Singleton
{
  private static Singleton instance = null;
  private Singleton()
  {
  }

  public static Singleton instance
  {
    get
    {
      if (instance == null)
      {
        instance = new Singleton();
      }
      return instance;
    }
  }
}
ログイン後にコピー

このメソッドはスレッドセーフではありません。if (instance == null) の場合、同時に 2 つのスレッドが実行され、2 つの異なるインスタンスが作成され、以前に取得されたインスタンスが置き換えられます。

2 番目の単純なスレッドセーフ実装

public sealed class Singleton
{
  private static Singleton instance = null;
  private static readonly object padlock = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      lock (padlock)
      {
        if (instance == null)
        {
          instance = new Singleton();
        }
        return instance;
      }
    }
  }
}
ログイン後にコピー

実装 1 と比較して、このバージョンでは、インスタンスを呼び出す前にインスタンスにロックを追加する必要があるため、実装 1 ではスレッドの競合が回避されます。ただし、インスタンスが呼び出されるたびにロックが使用されるため、この実装ではパフォーマンスがある程度低下します

ここでは新しいプライベート オブジェクトを使用していることに注意してください。シングルトンを直接ロックするのではなく、インスタンスの南京錠を使用してロック操作を実装します。これにより、潜在的なリスクが生じる可能性があります。この型はパブリックであるため、理論的には、これを直接ロックするとパフォーマンス上の問題が発生し、さらにはデッドロックが発生する可能性があります。 : C# では、同じスレッドでオブジェクトをロックすることができますが、異なるスレッドが同時にロックされると、スレッド待ちが発生したり、深刻なデッドロック状態が発生したりする可能性があります。そのため、ロックを使用する場合は、ロックを使用するようにしてください。ロックするクラス内のプライベート変数を選択すると、上記の状況が発生するのを回避できます

3重検証スレッドセーフティ実装

public sealed calss Singleton
{
  private static Singleton instance = null;
  private static readonly object padlock = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      if (instance == null)
      {
        lock (padlock)
        {
          if (instance == null)
          {
            instance = new Singleton();
          }
        }
      }
      return instance;
    }
  } 
}
ログイン後にコピー

この実装は、スレッドの安全性を確保しながら、インスタンスが呼び出されるたびにロック操作も回避します。 、ある程度の時間を節約できます。

ただし、この実装には欠点もあります。

1 は Java では機能しません。 (具体的な理由については原文をご覧ください。ここではあまり理解できません)

2 プログラマーは自分で実装するときに間違いを犯しがちです。このモードでコードを独自に変更する場合は、ダブル チェックのロジックが比較的複雑で、考えが不十分なために間違いを犯しやすいため、十分に注意してください。

ロックを使用しない 4 つのスレッドセーフな実装

public sealed class Singleton
{
  //在Singleton第一次被调用时会执行instance的初始化
  private static readonly Singleton instance = new Singleton();

  //Explicit static consturctor to tell C# compiler 
  //not to mark type as beforefieldinit
  static Singleton()
  {
  }

  private Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      return instance;
    }
  }
}
ログイン後にコピー

この実装は非常にシンプルでロックを使用しませんが、依然としてスレッドセーフです。ここでは、静的な読み取り専用のシングルトン インスタンスが使用されます。シングルトンが初めて呼び出されたときに、新しいインスタンスが作成されます。新しいインスタンスを作成するときのスレッドの安全性の保証は、.NET によって直接制御されます。また、AppDomaining 内で 1 回だけ作成されます。

この実装にはいくつかの欠点もあります:

1 インスタンスが作成されるタイミングは不明であり、Singleton への呼び出しにより事前にインスタンスが作成されます

2 静的コンストラクターの循環呼び出し。 2 つのクラス A と B があり、A の静的コンストラクターが B を呼び出し、B の静的コンストラクターが A を呼び出す場合、これら 2 つは循環呼び出しを形成し、プログラムが重大なクラッシュを引き起こす可能性があります。

3 Singleton の静的コンストラクターを手動で追加して、Singleton 型が beforefieldinit 属性で自動的に追加されないようにして、Singleton が初めて呼び出されたときにインスタンスが作成されるようにする必要があります。

4readonly 属性は実行時に変更できません。プログラムの実行中にインスタンスを破棄して新しいインスタンスを再作成する必要がある場合、この実装方法は満たされません。

5 つの完全な遅延インスタンス化

public sealed class Singleton
{
  private Singleton()
  {
  }

  public static Singleton Instance 
  {
    get
    {
      return Nested.instance;
    }
  }

  private class Nested
  {
    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Nested()
    {
    }

    internal static readonly Singleton instance = new Singleton();
  }
}
ログイン後にコピー

実装 5 は、実装 4 のラッパーです。これにより、インスタンスが Instance の get メソッドでのみ呼び出され、最初の呼び出しの前にのみ初期化されることが保証されます。これは、遅延読み込みを保証する実装 4 のバージョンです。

Six は .NET4 の Lazy 型を使用します

public sealed class Singleton
{
  private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());

  public static Singleton Instance 
  {
    get 
    {
      return lazy.Value;
    }
  }

  private Singleton()
  {
  }
}
ログイン後にコピー

.NET4 以降では、最も単純なコードを使用してシングルトンの遅延読み込み特性を保証します。

パフォーマンスの違い

前の実装では、スレッドの安全性とコードの遅延読み込みを強調しました。ただし、実際の使用では、シングルトン クラスの初期化に時間がかからない場合、または初期化シーケンスによってバグが発生しない場合、初期化にかかる時間は無視できるため、遅延初期化は必要ありません。

実際の使用シナリオでは、シングルトン インスタンスが頻繁に呼び出される場合 (ループ内など)、スレッド セーフの確保によって生じるパフォーマンスの消費はより注目に値します。

これらの実装のパフォーマンスを比較するために、これらの実装のシングルトンを 9 億回ループし、毎回インスタンス メソッドを呼び出して count++ 操作を実行し、100 万ごとに出力して、環境を実行するという小さなテストを作成しました。 MBP 上の Visual Studio for Mac です。結果は次のとおりです。

完全ではありません 145725つの実現ははいはい14295が6つのyesyes22875概要

スレッド セーフティ 遅延読み込み テスト実行時間 (ミリ秒)
実装 1 いいえ はい 15532
実績2 45803
3を実現する 15953
4を実現する
テスト方法は厳密ではありませんが、メソッド2が最も時間がかかるため、それは最も時間がかかることがわかります。毎回 lock を呼び出す必要があり、他のものよりもほぼ 3 倍多くなります。 2 位は .NET Lazy 型を使用した実装で、他の実装より約 2 分の 1 多くなっています。残りの 4 つは明らかな違いはありません。
一般に、上記で説明したさまざまなシングルトン実装方法は、今日のコンピューターのパフォーマンスではそれほど違いはありません。特に大量の同時呼び出しでインスタンスを呼び出す必要がない限り、パフォーマンスを考慮する必要があります。ロックの質問。

一般の開発者にとって、シングルトンを実装するには方法 2 または方法 6 を使用するだけで十分です。方法 4 と 5 では、C# の実行プロセスを十分に理解し、実装する際にある程度の習熟度が必要です。保存にはまだ制限があります。

引用

この記事の大部分は、C# でのシングルトン パターンの実装を翻訳したものであり、私自身の理解もいくつか追加されています。これは、静的読み取り専用フィールドの初期化と静的コンストラクターの初期化を検索したときに見つけたものです。ここで 2 人の著者に感謝の意を表したいと思います。

以上がC#シングルトンパターンの実装と性能比較例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート