【Go學習筆記11】併發(一)

zhongzhong05發表於2017-12-10

併發模型

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函式執行完成")
}
複製程式碼

上面的程式碼我們使用了互斥鎖來解決了資料爭用的問題。

相關文章