go併發程式設計基礎
阿新 • • 發佈:2019-02-09
go被譽為網際網路的C語言,它的同步其實還是基於傳統語言,但是它的優勢在於語言層面實現了協程支援,goroutine和channel組成的CSP模型,大大減少了併發程式設計開發難度,簡單記下心得供有需要朋友參考。
go中開闢新執行緒使用go 關鍵字即可,同步方法分為傳統方法和基於通道(channel)的方法,通道包括無緩衝和有緩衝通道,這些同步方法結合保證了go併發程式設計的靈活性。
1.傳統方法
傳統方法包括有鎖程式設計和無鎖程式設計,有鎖指使用鎖來保證程式碼同步,無鎖程式設計使用原子程式設計來保證程式碼同步。
如下,開闢兩個go執行緒,同時做加操作,因為涉及到競態條件,對於全域性變數count=0結果為1.
func test_count1(name string){
defer g.Done()
fmt.Println(name, " run...")
v := count
runtime.Gosched()
v++
count=v
fmt.Println(name, "done... count=", v)
}
為了解決這個問題,就必須保證count增加過程中的不可被分割,使用加鎖程式設計如下:
//鎖同步 func test_count2(name string){ defer g.Done() fmt.Println(name, " run...") m.Lock() v := count runtime.Gosched() v++ count=v m.Unlock() fmt.Println(name, "done... count=", v) }
也可以使用原子程式設計如下:
//原子同步
func test_count3(name string){
defer g.Done()
fmt.Println(name, " run...")
atomic.AddInt32(&count, 1)
runtime.Gosched()
}
2.無緩衝通道
無緩衝通道保證資料傳送和接收是同時的,適合用於多個執行緒併發執行且相互之間有一定互斥關係的,每個時刻只能有一個執行緒持有通道資料。
比如如下,兩個選手相互擊球,通道是球,每個時刻一定只有一個選手能持有球,通過將資料傳給通道來模擬傳球過程,這也是channel的特質——將資料同步轉成goroutine間的資料傳遞
//無緩衝通道1-球賽對打
func Play(name string, ball chan int){
defer w.Done()
for {
v, ok := <-ball
if !ok {
fmt.Println(name, " win the game.")
break
}
n := rand.Intn(100)
if n%13 == 0 {
fmt.Println(name, " lose the game.")
close(ball)
break
}
v++;
fmt.Println(name, " kick the ball.")
ball <- v
}
}
func testPlay(){
w.Add(2)
ball := make(chan int)
go Play("wenzhou", ball)
go Play("wenwei", ball)
ball <- 1
w.Wait()
}
還可以比喻成接力賽,每次只有一個選手能夠獲取接力球,如下,通過傳遞資料給通道來模擬選手接力的過程。
//無緩衝通道2-接力賽
func Run(ball chan int){
v, ok := <-ball
if !ok{
return
}
fmt.Printf("Runner %d start to run\n", v)
if v!=4 {
v++
fmt.Printf("Runner %d ready to run\n", v)
go Run(ball)
}
//Running
fmt.Println("...")
time.Sleep(time.Duration(1)*time.Second)
if v==4 {
fmt.Printf("Runner %d Finished, race over\n", v)
w.Done()
return
}
fmt.Printf("Runner %d give ball to Runner %d\n", v-1, v)
ball <- v
}
func testRun(){
w.Add(1)
ball := make(chan int)
go Run(ball)
ball <- 1
w.Wait()
}
3.有緩衝通道
有緩衝通道的可以起到快取和佇列的作用,如下採用通道來完成任務排隊的功能,很輕易的實現多路處理器的功能。
//帶緩衝通道
func Worker(task chan string, worker int){
defer w.Done()
for {
t, ok := <-task
if !ok {
fmt.Printf("Worker %d Shutdwon\n", worker)
break
}
fmt.Printf("Worker %d - Started:%s...\n", worker, t)
time.Sleep(time.Duration(rand.Intn(100))*time.Microsecond)
fmt.Printf("Worker %d - Completed:%s...\n", worker, t)
}
}
func testWorker(){
//緩衝通道
tasks := make(chan string, NUM_TASK)
//建立Worker,等待處理任務
w.Add(NUM_WORKER)
for i:=1; i<=NUM_WORKER; i++ {
go Worker(tasks, i)
}
//拋任務處理
for t:=1; t<=NUM_TASK; t++ {
tasks <- fmt.Sprintf("Task-%d", t)
}
//關閉通道,等待處理完畢,注意go機制保證關閉通道後剩餘任務可以被處理完畢
close(tasks)
w.Wait()
}
演示程式碼下載連結