併發模型
go語言中的併發是指,可以讓一個函式獨立於其他函式單獨執行的能力。當一個函式建立為一個goroutine時,go語言會把這個函式當作一個可以獨立執行的單元。 go語言中的併發模型是一種訊息傳遞模型。不像java中的記憶體模型,需要通過加鎖來實現同步。在go語言中,每一個goroutine之間同步和傳遞資料是通過通道來實現的。
建立goroutine
package main
import (
"fmt"
)
func main(){
//宣告一個匿名函式,並建立一個goroutine
go func(){
fmt.Println("來自goroutine。。。")
}()
fmt.Println("來自main函式")
}
//來自main函式
或
//來自goroutine。。。
//來自main函式
複製程式碼
多次執行上面的程式碼,會發現有時能夠輸出“來自goroutine。。。”這句,而有時只會輸出“來自main函式”,這是因為如果main函式先執行完成,程式就會退出,那麼gorouthine就無法繼續執行了,導致沒有輸出。
看下面的例子:
package main
import (
"time"
"fmt"
)
func main(){
//宣告一個匿名函式,並建立一個goroutine
go func(){
fmt.Println("來自goroutine。。。")
}()
//暫停2秒
time.Sleep( 2 * time.Second)
fmt.Println("來自main函式")
}
//來自goroutine。。。
//來自main函式
複製程式碼
上面的程式碼通過呼叫time.Sleep方法來讓程式暫停執行2秒,這樣,goroutine中的程式碼才能夠執行,並且輸出。
package main
import (
"runtime"
"time"
"fmt"
)
var num int = 10
func A(){
for i:=1 ; i<=5 ; i++{
temp := num
runtime.Gosched()
temp--
num = temp
}
}
func B(){
for i:=1 ; i<=5 ; i++{
temp := num
//讓出當前處理器
runtime.Gosched()
temp--
num = temp
}
}
func main(){
//分配一個邏輯處理器給排程器使用
runtime.GOMAXPROCS(1)
go A()
go B()
//暫停1秒,讓gorouthine執行完
time.Sleep( 100 * time.Millisecond)
fmt.Println("num=",num)
fmt.Println("main函式執行完成")
}
//num= 5
//main函式執行完成
複製程式碼
上面的程式碼執行完成之後,結果是5。這是因為在每一次執行num-1的時候,呼叫了runtime.Gosched()方法,讓當前的goroutine退出當前執行緒給其他goroutine執行的機會。這樣是為了讓資料爭用的問題更加明顯。
解決資料爭用問題
package main
import (
"runtime"
"time"
"fmt"
"sync/atomic"
)
var num int64 = 10
func A(){
for i:=1 ; i<=5 ; i++{
//使用原子操作對num減1
atomic.AddInt64(&num,-1)
runtime.Gosched()
}
}
func B(){
for i:=1 ; i<=5 ; i++{
//使用原子操作對num減1
atomic.AddInt64(&num,-1)
runtime.Gosched()
}
}
func main(){
runtime.GOMAXPROCS(1)
go A()
go B()
//暫停1秒,讓gorouthine執行完
time.Sleep( 100 * time.Millisecond)
fmt.Println("num=",num)
fmt.Println("main函式執行完成")
}
//num= 0
//main函式執行完成
複製程式碼
上面的程式碼使用atomic包下面的原子操作來解決資料爭用的問題。除了使用原子操作來解決資料爭用問題之外,我們還可以使用鎖來解決。看下面的程式碼:
package main
import (
"sync"
"runtime"
"time"
"fmt"
)
var num int = 10
var mutex sync.Mutex
func A(){
for i:=1 ; i<=5 ; i++{
//鎖住
mutex.Lock()
temp := num
runtime.Gosched()
temp--
num = temp
//解鎖
mutex.Unlock()
}
}
func B(){
for i:=1 ; i<=5 ; i++{
//鎖住
mutex.Lock()
temp := num
runtime.Gosched()
temp--
num = temp
//解鎖
mutex.Unlock()
}
}
func main(){
runtime.GOMAXPROCS(1)
go A()
go B()
//暫停1秒,讓gorouthine執行完
time.Sleep( 100 * time.Millisecond)
fmt.Println("num=",num)
fmt.Println("main函式執行完成")
}
複製程式碼
上面的程式碼我們使用了互斥鎖來解決了資料爭用的問題。