シングルトン実装スキームに移行

誰もがシングルトンパターンをよく知っており、次のように定義されています。クラスは1つのオブジェクト(またはインスタンス)のみを作成でき、このクラスはシングルトンクラスです。このデザインパターンは、シングルトンデザインパターンまたはシングルトンと呼ばれます。略してパターン。

シングルトンモードは比較的簡単に理解できますが、実際に実装する際に考慮する必要のある詳細が多数あります。一般的な考慮事項は次のとおりです。

  1. コンストラクターは、新規によるインスタンスの外部作成を回避するために、プライベートアクセス権を持っている必要があります

  2. オブジェクトを作成するときは、スレッドセーフの問題を考慮してください

  3. 遅延読み込みをサポートするかどうかを検討する

  4. getInstance()のパフォーマンスが高いかどうかを検討します(ロックするかどうか)

コード

文法が異なり、これらの点への注意度も異なります。Go言語の場合、sync.Once.Doを使用した記述方法は次のとおりです。この関数の目的は、1回だけ実行することです。

だから私たちは書くことができます:

type Single struct {
    
    

}
var (
   once sync.Once
   single     *Single
)


func GetSingleInstance() *Single {
    
    
   once.Do(func() {
    
    
      single = &Single{
    
    }
   })
   return single
}

リクエストの数に関係なく、Singleのインスタンスは1つだけです。

テスト

次に、シナリオについて考えてみましょう。100個のリクエストが突然GetSingleInstanceインターフェイスを同時にリクエストした場合、これらのリクエストはDoが実行されるのを待つのでしょうか、それともDoに関係なくシングルを返すだけでしょうか。

理論的には、Doの実行が完了するまで待つ必要があります。そうしないと、返されるシングルが空になり、重大なエラーが発生します。そう思いますが、テストしてみましょう。

package main

import (
   "fmt"
   "strconv"
   "sync"
   "time"
)

func main() {
    
    
   var once sync.Once
   onceBody := func() {
    
    
      fmt.Println("Only once start")
      time.Sleep(time.Second * 5)
      fmt.Println("Only once end")
   }

   for i := 0; i < 5; i++ {
    
    
      j := i
      go func(int) {
    
    
         fmt.Println(j)
         once.Do(onceBody)
         fmt.Println("lll" + strconv.Itoa(j))
      }(j)

   }
   fmt.Println("finished")
   time.Sleep(time.Second * 10)
}

テスト計画は非常に単純です。5つのゴルーチンを開始し、同時にDoを呼び出します。一度、Bodyが5秒間スリープを設定したら、出力を確認するだけで、ブロックされるかどうかを判断できます。

実行結果は以下のとおりです。

➜は、実行main.goが行くMYPROJECT
終了
0を
一度だけ起動する
2
4
3
1を
一度だけ、最後
lll2
lll4
lll0
lll1
lll3

Doが実行された後にのみすべてのゴルーチンが出力されることがわかります。これは、Doが呼び出されることを示していますが、実際に実行されるのは1つだけです。実際の実行が完了する前に、他のゴルーチンはブロックされます。

実際、隠れたリスクがあります。Doによって実行される関数に時間がかかると、多数のゴルーチンが蓄積されます。これは、プログラミング時に考慮する必要があります。

実装

Do機能はどのように実装されていますか?ソースコードを見てみましょう:

func (o *Once) Do(f func()) {
    
    
   if atomic.LoadUint32(&o.done) == 0 {
    
    
      // Outlined slow-path to allow inlining of the fast-path.
      o.doSlow(f)
   }
}
func (o *Once) doSlow(f func()) {
    
    
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
    
    
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}

複数のゴルーチンがdone値が0であることを確認する場合、doSlowと入力すると、1つのゴルーチンのみがロックを取得し、他のゴルーチンはブロックされません。

実用的なものは、より一般的な技術であり、主に相互排他ロック、セマフォ、および延期ですが、設計は依然として非常に巧妙です。これはGoの利点でもあります。競合を解決するためのロックの使用は高速で安全かつ便利ですが、パフォーマンスの問題を考慮する必要があります。

やっと

私の記事が気に入ったら、私の公式アカウント(プログラマーMala Tang)をフォローしてください。

私の個人的なブログは次のとおりです:https://shidawuhen.github.io/

以前の記事のレビュー:

技術

  1. Goデザインパターン(4)-コードの記述
  2. Goデザインパターン(3)-デザイン原則
  3. Goデザインパターン(2)-オブジェクト指向分析と設計
  4. 支払いアクセスの一般的な問題
  5. HTTP2.0基本チュートリアル
  6. Goデザインパターン(1)-文法
  7. MySQL開発仕様
  8. HTTPS構成の戦闘
  9. Goチャネル実装の原則
  10. Goタイマーの実装原理
  11. HTTPS接続プロセス
  12. 電流制限の実現2
  13. スパイクシステム
  14. 分散システムとコンセンサスプロトコル
  15. マイクロサービスのサービスフレームワークとレジストリ
  16. Beegoフレームワークの使用法
  17. マイクロサービスについて話す
  18. TCPパフォーマンスの最適化
  19. 電流制限の実現1
  20. Redisは分散ロックを実装しています
  21. Golangソースコードのバグ追跡
  22. トランザクションの原子性、一貫性、耐久性の実現原理
  23. 詳細なCDNリクエストプロセス
  24. 一般的なキャッシュ手法
  25. サードパーティの支払いに効率的に接続する方法
  26. ジンフレームワークの簡潔なバージョン
  27. InnoDBのロックとトランザクションの簡単な分析
  28. アルゴリズムの概要

研究ノート

  1. 原則として
  2. Zi Zhi Tong Jian
  3. アジャイル革命
  4. あなたの記憶を行使する方法
  5. 単純なロジック-読んだ後
  6. 熱風-読んだ後
  7. 論語-読んだ後の考え
  8. 孫子の戦争の芸術-読んだ後の考え

考え

  1. 自由主義に対して
  2. 実践理論
  3. 自分の基準を評価する
  4. サーバーチームの休日の義務計画
  5. プロジェクトプロセス管理
  6. プロジェクト管理に関するいくつかの見解
  7. プロダクトマネージャーに関するいくつかの考え
  8. プログラマーのキャリア開発についての考え
  9. コードレビューについて考える
  10. マークダウンエディターの推奨事項-typora

おすすめ

転載: blog.csdn.net/shida219/article/details/114818645