深さ解析Onceソース

3478 ワード

目次

  • syncとは何ですか.Once
  • syncの使用方法Once
  • ソースコード分析
  • 文章は公衆番号【マイモコード】から始まる
    https://mp.weixin.qq.com/s/b89PmljELaPaVuLw-YIQKg

    syncとは何ですか。Once


    Onceは、1回の動作のみを実行するために使用することができ、通常、単一のオブジェクトの初期化シーンに使用される.
    Onceは、単一のリソースを初期化したり、一度だけ共有リソースを初期化したり、テスト時にテストリソースを初期化したりするために同時アクセスするためによく使用されます.
    sync.Onceは1つのメソッドDoのみを暴露し、Doメソッドを複数回呼び出すことができますが、最初にDoメソッドを呼び出すとfパラメータが実行されます.ここでfはパラメータなしで戻り値のない関数です.

    syncの使用方法Once


    私が担当しているプロジェクトの1つについて言えば、プロジェクトの構成はサードパーティのプラットフォームに掛けられているので、プロジェクトの起動時にリソースの構成を取得する必要があります.構成が1回しか取得できないことを保証する方法が必要ですので、syncを使用することを考えています.Onceでリソースを取得します.これにより、一度だけ実行されるリソース取得メソッドを他の場所で呼び出すことを防止することができる.
    次は簡単にDemoを書いてsyncを実証します.Onceの使い方
    package main
    import (
       "fmt"
       "sync"
    )
    var once sync.Once
    var con string
    func main() {
       once.Do(func() {
          con = "hello Test once.Do"
       })
       fmt.Println(con)
    }

    コードの説明:
    コードは比較的簡単で、Doメソッドを呼び出すことによって、閉パッケージ方式を採用し、文字列(「hello Test once.Do」)をconに付与し、さらに値を印刷するsyncである.Onceの使用は、比較的使いやすいです.
    しかし、私たちが一つの方法やフレームワークを使うとき、それを手のひらのように知らなければ、いつも頼りにならないので、心が落ち着かないような気がします.そのためにsyncについてお話ししましょうOnceのソースコードが実現し、彼を逃げ場がない.

    ソース分析


    次にsyncを分析します.Doはどのように実現され、パッケージsyncの下に保存されていますか.goファイルでは、ソースコードは次のとおりです.
    // sync/once.go
    
    type Once struct {
       done uint32 //  0 ,1 
       m    Mutex 
    }
    func (o *Once) Do(f func()) {
       //  done 0, 0, , doSlow() 
       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()
       //    done 
       if o.done == 0 {
          //  f() , done 1
          defer atomic.StoreUint32(&o.done, 1)
          //  f() 
          f()
       }
    }

    次は二つの部分に分けて分析します.第一部分はOnceの構造体構成構造で、第二部分はDo関数の実現原理で、私はコードに注釈をつけて、心を込めて読んでから収穫があることを保証します.

    こうぞうたい

    type Once struct {
       done uint32 //  0 ,1 
       m    Mutex 
    }

    まずstruct構造体Onceを定義し、doneとmの2つのメンバー変数を格納します.
    doneメンバー変数
  • 1は、リソースが初期化されていないことを示し、
  • をさらに初期化する必要がある.
  • 0は、リソースが初期化されていることを示し、初期化する必要はなく、直接戻ると
  • になる.
    mメンバー変数
  • 複数のgoroutineがdoSlow()を呼び出してリソースを初期化する際に、リソースが複数回初期化することを防止するため、Mutexロック機構を用いることで、
  • を1回のみ初期化することを保証する.

    Do

    func (o *Once) Do(f func()) {
       //  done 0, 0, , doSlow() 
       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()
       //    done 
       if o.done == 0 {
          //  f() , done 1
          defer atomic.StoreUint32(&o.done, 1)
          //  f() 
          f()
       }

    Do関数を呼び出すと、まずdone値が0であるかどうかを判断し、1であれば、入力された匿名関数f()が実行されたことを示し、再実行する必要はない.0であれば、入力された匿名関数f()がまだ実行されていないことを示すdoSlow()関数を呼び出して初期化する.
    doSlow()関数では、同時goroutineがこの関数に入ると、f()匿名関数を実行するgoroutineが1つしかないことを保証します.このため,反発ロックを加えて1つのgoroutineのみを初期化することを保証するとともに,ダブルチェックのメカニズム(double-checking)を採用し,o.doneが0であるか否かを再判断し,0であれば初めて実行し,実行が完了するとo.doneを1に設定してロックを解放する必要がある.
    このとき複数のgoroutineが同時にdoSlowメソッドに入ったとしても、ダブルチェックのメカニズムのため、後続のgoroutineはo.doneの値が1であることを見て、fを再実行することはありません.
    これにより、同時goroutineがfの完了を待つことが保証され、fが複数回実行されないことが保証される.
    文章も更新され続け、微信で「マイモcoding」を検索して最初に読むことができます.毎日良質な文章、大工場の経験、大工場の経験を分かち合い、面接を支援することは、プログラマー一人一人が注目すべきプラットフォームである.