條件變量的作用並不是保證在同一時刻僅有一個線程訪問某一個共享數據,而是在對應的共享數據的狀態發生變化時,通知其他因此而被阻塞的線程。
- 條件變量與互斥量組合使用
- 互斥量爲共享數據的訪問提供互斥支持
- 條件變量就狀態的變化向相關線程發出通知
三種操作方法
- 等待通知: wait
- 阻塞當前線程,直到收到該條件變量發來的通知
- 單發通知: signal
- 讓該條件變量向至少一個正在等待它的通知的線程發送通知,表示共享數據的狀態已經改變。
- 廣播通知: 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
}
因爲一個數據塊只能有一個讀操作讀取,因此我們使用條件變量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
}