golang互斥鎖的一個案例

package main

import (
	"fmt"
	"runtime"
	"sync"
)

type Counter struct {
	mu sync.Mutex
	x  int64
}

func (c *Counter) Inc() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.x++
}
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	c := Counter{}
	var wait sync.WaitGroup
	wait.Add(4)
	for k := 4; k > 0; k-- {
		go func() {
			for i := 2500000; i > 0; i-- {
				c.Inc()
			}
			wait.Done()
		}()
	}
	wait.Wait()
	fmt.Println(c.x)
}

上面是一個比較常見的例子,以下是整理的一些解釋golang

+++++++++++++++++++++++++++++++++++++++atom

golang中sync包實現了兩種鎖Mutex (互斥鎖)和RWMutex(讀寫鎖),其中RWMutex是基於Mutex實現的,只讀鎖的實現使用相似引用計數器的功能.spa

type Mutexcode

    func (m *Mutex) Lock()源碼

    func (m *Mutex) Unlock()it

type RWMutexast

    func (rw *RWMutex) Lock()class

    func (rw *RWMutex) RLock()import

    func (rw *RWMutex) RLocker() Locker權限

    func (rw *RWMutex) RUnlock()

    func (rw *RWMutex) Unlock()

 

其中Mutex爲互斥鎖,Lock()加鎖,Unlock()解鎖,使用Lock()加鎖後,便不能再次對其進行加鎖,直到利用Unlock()解鎖對其解鎖後,才能再次加鎖.適用於讀寫不肯定場景,即讀寫次數沒有明顯的區別,而且只容許只有一個讀或者寫的場景,因此該鎖葉叫作全局鎖.

func (m *Mutex) Unlock()用於解鎖m,若是在使用Unlock()前未加鎖,就會引發一個運行錯誤.

已經鎖定的Mutex並不與特定的goroutine相關聯,這樣能夠利用一個goroutine對其加鎖,再利用其餘goroutine對其解鎖.

正常運行例子:

package main  
  
import (  
    "fmt"  
    "sync"  
)  
  
func main() {  
    var l *sync.Mutex  
    l = new(sync.Mutex)  
    l.Lock()  
    defer l.Unlock()  
    fmt.Println("1")  
}  
結果輸出:1

當Unlock()在Lock()以前使用時,便會報錯

package main  
  
import (  
    "fmt"  
    "sync"  
)  
  
func main() {  
    var l *sync.Mutex  
    l = new(sync.Mutex)  
    l.Unlock()  
    fmt.Println("1")  
    l.Lock()  
}  
運行結果: panic: sync: unlock of unlocked mutex

當在解鎖以前再次進行加鎖,便會死鎖狀態

package main  
  
import (  
    "fmt"  
    "sync"  
)  
  
func main() {  
    var l *sync.Mutex  
    l = new(sync.Mutex)  
    l.Lock()  
    fmt.Println("1")  
    l.Lock()  
}  
運行結果:  1  
    
  fatal error: all goroutines are asleep - deadlock!

RWMutex是一個讀寫鎖,該鎖能夠加多個讀鎖或者一個寫鎖,其常常用於讀次數遠遠多於寫次數的場景.

  func (rw *RWMutex) Lock()  寫鎖,若是在添加寫鎖以前已經有其餘的讀鎖和寫鎖,則lock就會阻塞直到該鎖可用,爲確保該鎖最終可用,已阻塞的 Lock 調用會從得到的鎖中排除新的讀取器,即寫鎖權限高於讀鎖,有寫鎖時優先進行寫鎖定
  func (rw *RWMutex) Unlock() 寫鎖解鎖,若是沒有進行寫鎖定,則就會引發一個運行時錯誤.

package main  
  
import (  
    "fmt"  
    "sync"  
)  
  
func main() {  
    var l *sync.RWMutex  
    l = new(sync.RWMutex)  
    l.Unlock()  
    fmt.Println("1")  
    l.Lock()  
}  
運行結果:panic: sync: unlock of unlocked mutex

 func (rw *RWMutex) RLock() 讀鎖,當有寫鎖時,沒法加載讀鎖,當只有讀鎖或者沒有鎖時,能夠加載讀鎖,讀鎖能夠加載多個,因此適用於"讀多寫少"的場景

func (rw *RWMutex)RUnlock() 讀鎖解鎖,RUnlock 撤銷單次 RLock 調用,它對於其它同時存在的讀取器則沒有效果。若 rw 並無爲讀取而鎖定,調用 RUnlock 就會引起一個運行時錯誤(注:這種說法在go1.3版本中是不對的,例以下面這個例子)。

package main  
  
import (  
    "fmt"  
    "sync"  
)  
  
func main() {  
    var l *sync.RWMutex  
    l = new(sync.RWMutex)  
    l.RUnlock()    //1個RUnLock  
    fmt.Println("1")  
    l.RLock()                
}  
  
運行結果:1  
可是程序中先嚐試 解鎖讀鎖,而後才加讀鎖,可是沒有報錯,而且可以正常輸出.

分析:go1.3版本中出現這種狀況的緣由分析,經過閱讀源碼能夠很清晰的獲得結果

func (rw *RWMutex) RUnlock() {  
    if raceenabled {  
        _ = rw.w.state  
        raceReleaseMerge(unsafe.Pointer(&rw.writerSem))  
        raceDisable()  
    }<span style="color:#FF0000;">  
    if atomic.AddInt32(&rw.readerCount, -1) < 0 { //readercounter初始值爲0,調用RUnLock以後變爲-1,繼續往下執行  
        // A writer is pending.  
        if atomic.AddInt32(&rw.readerWait, -1) == 0 { //此時readerwaiter變爲1,1-1以後變爲0,能夠繼續之後的操做.</span>  
            // The last reader unblocks the writer.  
            runtime_Semrelease(&rw.writerSem)  
        }  
    }  
    if raceenabled {  
        raceEnable()  
    }  
}

當RUnlock多於RLock多個時,便會報錯,進入死鎖.實例以下:

package main  
  
import (  
    "fmt"  
    "sync"  
)  
  
type s struct {  
    readerCount int32  
}  
  
func main() {  
    l := new(sync.RWMutex)  
    l.RUnlock()  
    l.RUnlock()        //此處出現死鎖  
    fmt.Println("1")  
    l.RLock()  
}  
運行結果:  
1  
    
  fatal error: all goroutines are asleep - deadlock!

總結:

因此在go1.3版本中,運行過程當中容許RUnLock早於RLock一個,也只能早於1個(注:雖然代碼容許,可是強烈不推薦使用),而且在早於以後必須利用RLock進行加鎖才能夠繼續使用.

相關文章
相關標籤/搜索