并發 concurrency
- Goroutine 通過通信來共享內存,而不是通過共享內存來通信
- Channel是Goroutine的溝通橋梁,引用類型,大都是阻塞同步的
- make創建,close關閉
- for range迭代操作channel,可以設置單向或者雙向管道,緩存大小,為填滿之前不會堵塞
- Select
- 可以處理一個或者多個channel的發送和接收
- 同時有多個可以用的chnnel隨機順序處理
- 可設置空的select阻塞main函數,可設置超時
- 單向管道 chan<-int只發送同道,<-chan int,只接受通道
func initV(out chan<- int) {
for i := 0; i < 1000; i++ {
out <- i
}
close(out)
}
func squar(in <-chan int, out chan<- int) {
for i := range in {
out <- i * i;
}
close(out)
}
func print(in <-chan int) {
for i := range in {
fmt.Println("value is:", i)
}
}
x:=make(chan int)
y:=make(chan int)
go initV(x)
go squar(x,y)
print(y)
chan異步處理循環打印
func ChanRange(index int,c chan bool) {
a := 0
for i := 0; i < 100000000; i++ {
a += i
}
fm.Println("a value is:",index, a)
c<-true//設置chan值
}
func goChan() {//達到異步執行
runtime.GOMAXPROCS(1)/設置cpu最大可用核數
c := make(chan bool,10)//設置緩存大小,有緩存的時候是異步的,無緩存是同步堵塞
for i := 0; i < 10; i++ {
go my.ChanRange(i,c)//開啟一個新的goroutine執行
}
for i:=0;i<10;i++{
<-c//取出chan值
}
}
sync異步循環打印
func SyncRange(index int,wg *sync.WaitGroup) {
a := 0
for i := 0; i < 100000000; i++ {
a += i
}
fm.Println("a value is:",index, a)
wg.Done()//執行完畢一個,線程池就會減少一個線程
}
func goSync() {//達到異步執行
//runtime.GOMAXPROCS(runtime.NumCPU())/設置cpu最大可用核數
wg:=sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go my.SyncRange(i,&wg)
}
wg.Wait()
}
并發不是并行,==老的版本默認==,Go所有的goroutines只能在一個線程里跑,也就是說,設置核數為1地,goChan方法不是并行的,是并發的,==新版本默認==把runtime.GOMAXPROCS(runtime.NumCPU())設置最大的,那么他就是并行的
- 兩個隊列,一個Coffee機器,那是并發
- 兩個隊列,兩個Coffee機器,那是并行
package main
import (
"fmt"
)
func say(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func main() {
go say("world") //開一個新的Goroutines執行
for {
}
}
這里Go仍然在使用單核,for死循環占據了單核CPU所有的資源,而main線和say兩個goroutine都在一個線程里面,所以say沒有機會執行
允許Go使用多核(runtime.GOMAXPROCS)
手動顯式調動runtime包(runtime包是goroutine的調度器),
- Gosched 讓出cpu
- NumCPU 返回當前系統的CPU核數量
- GOMAXPROCS 設置最大的可同時使用的CPU核數
- Goexit 退出當前goroutine(但是defer語句會照常執行)
func loop() {
for i := 0; i < 10; i++ {
runtime.Gosched() // 顯式地讓出CPU時間給其他goroutine
fmt.Printf("%d ", i)
}
quit <- 0
}
func main() {
go loop()
go loop()
for i := 0; i < 2; i++ {
<- quit
}
}
- 上面代碼這種主動讓出CPU時間的方式仍然是在單核里跑。但手工地切換goroutine導致了看上去的“并行”,stackoverflow的 解釋:https://stackoverflow.com/questions/13107958/what-exactly-does-runtime-gosched-do
當一個goroutine發生阻塞,Go會自動地把與該goroutine處于同一系統線程的其他goroutines轉移到另一個系統線程上去,以使這些goroutines不阻塞,也就是說,goroutine不阻塞不放開CPU
package main
import (
"fmt"
"runtime"
)
var quit chan int = make(chan int)
func loop(id int) { // id: 該goroutine的標號
for i := 0; i < 10; i++ { //打印10次該goroutine的標號
fmt.Printf("%d ", id)
}
quit <- 0
}
func main() {
runtime.GOMAXPROCS(2) // 最多同時使用2個核
for i := 0; i < 3; i++ { //開三個goroutine
go loop(i)
}
for i := 0; i < 3; i++ {
<- quit
}
}
select的使用
- 多chan 處理
func TwoMoreChan() {
c1, c2 := make(chan bool), make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v, ok := <-c1:
if !ok {
o <- true
break
}
fm.Println("c1 value is:", v)
case v, ok := <-c2:
if !ok {
o <- true
break
}
fm.Println("c2 value is:", v)
}
}
}()
c1 <- false
c2 <- 0
c1 <- true
c2 <- 2
close(c1)
}
- 設置超時處理
func SelectTime() {
c := make(chan bool)
select {
case v := <-c:
fm.Println("c value is:", v)
case <-time.After(3 * time.Second):
fm.Println("time out")
}
}