同步lock
sync
先介紹一下 golang 的 package sync 他有實現兩種鎖 Mutex & RWMutex,RWMutex 是基於 Mutex實現的
sync.Mutex
又稱全局鎖、互斥鎖,使用Lock() 後 便不能在對它加鎖,直到Unlock()呼叫,反之在Unlock()呼叫之前,沒有Lock()執行過,會噴錯
var l sync.Mutex
var a string
func f() {
a = "hello, world"
l.Unlock()
}
func main() {
l.Lock()
go f()
l.Lock()
print(a)
}
第二個l.Lock 會在那邊等待第一個 l.Lock 被 f() 裡面的 l.Unlock 解鎖後才會繼續往下執行
sync.RWMutex
可以稱為讀寫鎖,可以加多個讀鎖或是一個寫鎖,呼叫寫鎖時 Lock(),如果有其他的讀鎖或是寫鎖,則Lock()或阻塞到該鎖可以用為止,就是寫鎖時優先寫鎖
RLock()
var m *sync.RWMutex
func main() {
m = new(sync.RWMutex)
go read(1)
go read(2)
//為了等待其他goroutine 跑完
time.Sleep(10*time.Second)
}
func read(i int) {
println(i,"read start")
m.RLock()
println(i,"reading")
time.Sleep(1*time.Second)
m.RUnlock()
println(i,"read over")
}
Lock()
var m *sync.RWMutex
func main() {
m = new(sync.RWMutex)
go write(1)
go read(2)
go write(3)
//為了等待其他 goroutine 跑完
time.Sleep(10*time.Second)
}
func read(i int) {
println(i,"read start")
m.RLock()
println(i,"reading")
time.Sleep(1*time.Second)
m.RUnlock()
println(i,"read over")
}
func write(i int) {
println(i,"write start")
m.Lock()
println(i,"writing")
time.Sleep(1*time.Second)
m.Unlock()
println(i,"write over")
}
sync.WaitGroup
func main() {
var wg sync.WaitGroup
//所有要抓取的url
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://tw.yahoo.com/",
}
for _, url := range urls {
// 群組計數器
wg.Add(1)
// Launch a goroutine to fetch the URL.
go func(url string) {
// 這個 goroutine結束 遞減
defer wg.Done()
// 抓url
http.Get(url)
fmt.Println(url);
}(url)
}
// 等待全部response返回
wg.Wait()
fmt.Println("over");
}
Data Race
再來,有的前面的介紹我們進入一些比較時常見的應用,我們了解一下什麼是 data race 以下引自 wiki
A race condition or race hazard is the behavior of an electronic, software or other system where the output is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when events do not happen in the order the programmer intended. The term originates with the idea of two signals racing each other to influence the output first. Race conditions can occur in electronics systems, especially logic circuits, and in computer software, especially multithreaded or distributed programs.
簡單來說就是兩個進程都要修改一個共享內容時,在沒有併發控制的情況,只能仰賴兩個進程執行順序與時機,會導致結果錯誤。
用個簡單例子來說,下面是一個在其他語言很常見的 singleton 模式,如果要再這種 muilt thread 環境運行下,該怎麼確保資源不會重複建構
type Singleton struct{
Data int
}
var instance * Singleton
func GetInstance() * Singleton {
if instance == nil {
time.Sleep(5 * time.Second)
// 仿造一個耗費資源的建構情境
println("Init instance")
instance = &Singleton{42}
}
println("finish instance")
return instance
}
func main(){
go GetInstance()
go GetInstance()
// 等待上面兩個 goroutine 執行完
time.Sleep(7 * time.Second)
println("end")
}
加入前面講過得讀寫鎖,防止重複建構
var mutex = &sync.RWMutex{}
type Singleton struct{
Data int
}
var instance * Singleton
func GetInstance() * Singleton {
mutex.RLock()
if instance == nil {
mutex.RUnlock()
mutex.Lock()
defer mutex.Unlock()
if instance == nil {
time.Sleep(5 * time.Second)
// 仿造一個耗費資源的建構情境
println("Init instance")
instance = &Singleton{42}
}
println("finish instance")
return instance
}else {
defer mutex.RUnlock()
println("instance exist")
return instance
}
}
func main(){
go GetInstance()
go GetInstance()
// 等待上面兩個 goroutine 執行完
time.Sleep(7 * time.Second)
println("end")
}