C#

【C#】Singleton Pattern(シングルトンパターン)のスレッドセーフな書き方3選

アイキャッチ(c_sharp)

こんにちは!
なかむぅです。

ずっとコピペプログラマーだった私は

オブジェクト指向?なにそれ美味しいの?

って感じだったので、「オブジェクト指向」を学ぶために「Java言語で学ぶデザインパターン入門第3版」を購入しました。

そのデザインパターンの一つ「Singleton Pattern(シングルトンパターン)」をC#で書いていこうと思います。


我が社で一緒に働きませんか?

私が働く会社では、一緒に働いてくれるエンジニアを募集しています♪

我が社のココがアツイ!!
  • 未経験、第二新卒歓迎
  • 学歴・転職回数・離職期間不問
  • 残業は月15時間以下で残業代は全額支給
  • 経験者は月給23万円以上
  • 年収100万以上UP↑↑↑も可能
  • 長く働いていても年収が全然上がらないから、一気に年収を上げたい
  • 残業をしたくてしているわけではないので、残業代はしっかりと出して欲しい
  • やりたいことに挑戦させてもらえないから、自分がしてみたいことに挑戦したい
  • 通勤時間が長すぎるから、できるだけ短くしたい
  • もっと勉強したいから、書籍代など負担してくれるところに入りたい
  • 現場に駆り出されてからは放置プレイで、相談できる人も先輩も居ないので、自分の状況をちゃんと理解してくれるところで働きたい

上記内容に1つでも当てはまる場合は、ぜひご検討ください。

詳細ページ


Singleton Pattern(シングルトンパターン)

あなたは免許証を持っているとします。

運転はもちろん、身分証明としても使えるので、この世に存在するあなたの免許証は1枚でないといけません。

なので免許証の発行は必ず1枚しか発行されないようになっています。

このようにプログラム上で必ず1つしか存在しないことを保証したい場合に使用するのがSingleton Pattern(シングルトンパターン)です。

プログラムのインスタンスのように何枚でも免許証が発行できたら困りますよね。

Singleton Patternの書き方

調べるとよく出てくるGoFのSingletonの書き方です。

コンストラクタがprivateになっているのが一番のポイント。

クラス内でしかインスタンスを生成できないようにすることで、他のクラスからインスタンスを生成できないことを保証しています。

インスタンスの生成はフィールドで直接newするか、GetInctanceのようなメソッド内でnullガードしてからnewする2つの方法がよく出てきます。


namespace SingletonPattern
{
	public class Singleton
	{
		private static Singleton _instance;
		// コンストラクタがprivateになっていることがポイント
		// 自分自身しかインスタンスを生成できないようにしてインスタンスが1つしか生成されないことを保証する
		private Singleton()
		{
			Console.WriteLine("インスタンスを生成しました。");
		}

		public static Singleton GetInctance()
		{
			// インスタンスが生成されてなかったら生成する
			// ただし、このやり方だと非同期処理で複数インスタンスが生成されてしまう可能性がある
			if (_instance == null)
			{
				_instance = new Singleton();
			}
			return _instance;
		}
	}
}

しかし、この方法だと非同期処理で複数のスレッドからアクセスしたときにインスタンスが複数生成されてしまう可能性があり、スレッドセーフではない実装になってしまいます。

次の見出しではスレッドセーフなSingletonの書き方をご紹介します。

Singleton Patternのスレッドセーフな書き方3選

こちらの内容はUdemyの「c# デザインパターン」で検索すると出てきます。

>>Udemyで検索する

lockを使ったSingleton

lockオブジェクトを生成して、複数のスレッドからアクセスした時にインスタンス生成前で他のスレッドを待機させる方法です。


namespace SingletonPattern
{
	public class Singleton
	{
		// Lockオブジェクトを作る
		private static readonly object _lock = new object();
		private static Singleton _instance;
		private Singleton()
		{
			Console.WriteLine("インスタンスを生成しました。");
		}

		private static Singleton GetInctance()
		{
			// 作ったlockオブジェクトで非同期処理のときに複数インスタンスが生成されないようにする
			lock (_lock)
			{
				if (_instance == null)
				{
					_instance = new Singleton();
				}

				return _instance;
			}
		}
	}
}

初期化子を使ったSingleton

プロパティから直接インスタンスを取得する方法です。

staticメンバーの初期化がランタイムにより厳密に一度しか行われないことが保証されるため、一度しかインスタンスが生成されないことが保証されます。


namespace SingletonPattern
{
	public class Singleton
	{
		// staticメンバーの初期化がランタイムにより厳密に一度しか行われないことが保証される
		public static Singleton Instance { get; } = new Singleton();
		private Singleton()
		{
			Console.WriteLine("インスタンスを生成しました。");
		}
	}
}

staticクラスなSingleton

シンプルにクラスをstaticしただけのクラスです。


namespace SingletonPattern
{
	public static class StaticSingleton
	{
		static StaticSingleton()
		{
			Console.WriteLine("staticクラスなSingleton");
		}
	}
}

我が社で一緒に働きませんか?

私が働く会社では、一緒に働いてくれるエンジニアを募集しています♪

我が社のココがアツイ!!
  • 未経験、第二新卒歓迎
  • 学歴・転職回数・離職期間不問
  • 残業は月15時間以下で残業代は全額支給
  • 経験者は月給23万円以上
  • 年収100万以上UP↑↑↑も可能
  • 長く働いていても年収が全然上がらないから、一気に年収を上げたい
  • 残業をしたくてしているわけではないので、残業代はしっかりと出して欲しい
  • やりたいことに挑戦させてもらえないから、自分がしてみたいことに挑戦したい
  • 通勤時間が長すぎるから、できるだけ短くしたい
  • もっと勉強したいから、書籍代など負担してくれるところに入りたい
  • 現場に駆り出されてからは放置プレイで、相談できる人も先輩も居ないので、自分の状況をちゃんと理解してくれるところで働きたい

上記内容に1つでも当てはまる場合は、ぜひご検討ください。

詳細ページ


まとめ

結構現場でよく使われている気がします。

ただのstaticクラスもあるので、どれがSingletonなのかは見極めが必要かも。

いやここは違うだろ

と思った方はコメントいただけると嬉しいです。

最後までお読みいただきありがとうございました!

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です