Golang同期:条件変数とロックを組み合わせて使用


条件変数の役割は、同じ時点で1つのスレッドのみが1つの共有データにアクセスすることを保証するのではなく、対応する共有データの状態が変化したときに、それによってブロックされた他のスレッドに通知することである.
  • 条件変数と反発量を組み合わせて
  • を用いる.
  • 反発量共有データへのアクセスに反発サポート
  • を提供する.
  • 条件変数は、状態の変化について関連スレッドに通知する
  • .
    3つの操作方法
  • 通知待ち:wait
  • は現在のスレッドをブロックし、この条件変数からの通知
  • が受信されるまでブロックする.
  • 単発通知:signal
  • は、共有データの状態が変化したことを示す通知を、その通知を待っている少なくとも1つのスレッドに条件変数を送信させる.

  • ブロードキャスト通知:broadcast
  • は、その通知を待っているすべてのスレッドに条件変数を通知する.


  • 宣言
    func NewCond(l Locker) *Cond

    前節のロック使用コードの変更
    Golang同期:ロックの使用例詳細転送ゲート:http://blog.csdn.net/liuxinmingcode/article/details/50044327
    Read()メソッドの改造は以下の通りです.
    func (df *myDataFile) Read() (rsn int64, d Data, err error){
        //          
        var offset int64
        //      
        df.rmutex.Lock()
        offset = df.roffset
        //      ,      +     
        df.roffset += int64(df.dataLen)
        //      
        df.rmutex.Unlock()
    
        //       ,           
        rsn = offset / int64(df.dataLen)
        bytes := make([]byte, df.dataLen)
        //   :   
        df.fmutex.RLock()
        defer df.fmutex.RUnlock()
    
        for {
            _, err = df.f.ReadAt(bytes, offset)
            if err != nil {
                if err == io.EOF {
                    //    fmutex    ,        
                    df.rcond.Wait()
                    continue
                }
            }
            return
        }
        d = bytes
        return
    }

    Write()メソッドの改造は以下の通りです.
    func (df *myDataFile) Write(d Data) (wsn int64, err error){
        //          
        var offset int64
        df.wmutex.Lock()
        offset = df.woffset
        df.woffset += int64(df.dataLen)
        df.wmutex.Unlock()
    
        //       ,          
        wsn = offset / int64(df.dataLen)
        var bytes []byte
        if len(d) > int(df.dataLen){
            bytes = d[0:df.dataLen]
        }else{
            bytes = d
        }
        df.fmutex.Lock()
        defer df.fmutex.Unlock()
        _, err = df.f.Write(bytes)
        //    
        df.rcond.Signal()
        return
    }

    1つのデータブロックに1つの読み取り操作しかないため、条件変数Signalメソッドを使用して、このために待機しているWaitメソッドを通知し、関連するGoroutineを起動します.
    もう一つ忘れてはいけないことがあります.rcondフィールドを初期化します.
    func NewDataFile(path string, dataLen uint32) (DataFile, error){
        //f, err := os.OpenFile(path, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0666)
        f,err := os.Create(path)
        if err != nil {
            fmt.Println("Fail to find", f, "cServer start Failed")
            return nil, err
        }
    
        if dataLen == 0 {
            return nil, errors.New("Invalid data length!")
        }
    
        df := &myDataFile{
            f : f,
            dataLen:dataLen,
        }
        //           (   ),    *sync.Cond      ,                Wait,Signal,Broadcast
        df.rcond = sync.NewCond(df.fmutex.RLocker())
        return df, nil
    }

    ソース:https://github.com/lxmgo/learngo