goroutine
它是go并發(fā)設(shè)計(jì)的核心荞驴。
goroutine就是協(xié)程琐谤,它比線程更小,十幾個(gè)goroutine在底層可能就是五六個(gè)線程魄懂。
go語言內(nèi)部實(shí)現(xiàn)了goroutine的內(nèi)存共享囤屹,執(zhí)行g(shù)oroutine只需極少的棧內(nèi)存(大概是4~5KB)。
main函數(shù)是主協(xié)程逢渔,如果主協(xié)程退出其他任務(wù)也不會(huì)執(zhí)行肋坚,程序直接退出。
runtime包
runtime.Gosched()
runtime.Gosched()用于讓出CPU時(shí)間片肃廓,調(diào)度器會(huì)重新進(jìn)行任務(wù)調(diào)度智厌,但是有可能還是分配到該任務(wù)
package main
import (
"fmt"
"runtime"
)
func main() {
//匿名子協(xié)程
go func(s string) {
for i := 0; i < 2; i++ {
fmt.Println(s)
}
}("world")
//主協(xié)程
for i := 0; i < 2; i++ {
runtime.Gosched()
fmt.Println("hello")
}
}
runtime.Goexit()
立即終止當(dāng)前協(xié)程,但是會(huì)保證已經(jīng)注冊(cè)的defer延遲調(diào)用都被執(zhí)行盲赊。
package main
import (
"fmt"
"time"
"runtime"
)
func main() {
//匿名子協(xié)程
go func() {
defer fmt.Println("A.defer")
//匿名函數(shù)
func() {
defer fmt.Println("B.defer")
//此時(shí)只有defer執(zhí)行
runtime.Goexit()
fmt.Println("B")
}()
fmt.Println("A")
}()
for {
time.Sleep(time.Second)
}
}
該程序執(zhí)行只會(huì)輸出
B.defer
A.defer
runtime.GOMAXPROCS()
設(shè)置并發(fā)執(zhí)行的CPU個(gè)數(shù)并返回之前的值
package main
import (
"runtime"
"fmt"
)
func main() {
n := runtime.GOMAXPROCS(3)
fmt.Println("n=%d\n",n)
//循環(huán)執(zhí)行2個(gè)
for{
go fmt.Print(0)
fmt.Print(1)
}
}
runtime. NumCPU()
返回系統(tǒng)的CPU數(shù)量
runtime. NumGoroutine()
返回正在執(zhí)行和排隊(duì)的所有任務(wù)總數(shù)
runtime.GOOS
返回操作系統(tǒng)
channel
創(chuàng)建
channel可以用內(nèi)置make()函數(shù)創(chuàng)建
make(chan 類型) //無緩沖的通道铣鹏,阻塞
make(chan 類型, 容量) //有緩沖的通道,非阻塞
傳輸數(shù)據(jù)
channel <- value //發(fā)送value到channel
<-channel //接收通道數(shù)據(jù)哀蘑,并丟棄
x := <-channel //通道取值并賦給x
x, ok := <-channel //ok是檢查通道是否關(guān)閉或者是否為空
遍歷
for foundOre := range c {
time.Sleep(2 * time.Second)
fmt.Println("Miner: Received " + strconv.Itoa(foundOre) + " from finder")
}
遍歷會(huì)阻塞直到有新數(shù)據(jù)發(fā)送到chanel诚卸,并且在遍歷成功后會(huì)繼續(xù)遍歷
關(guān)閉
close(c)
select
go語言提供了select關(guān)鍵字,可以監(jiān)聽channel上的數(shù)據(jù)流動(dòng)
語法與switch類似绘迁,區(qū)別是select要求每個(gè)case語句里必須是一個(gè)IO操作
select {
case <-chan1:
// 如果chan1成功讀到數(shù)據(jù)合溺,則進(jìn)行該case處理語句
case chan2 <- 1:
// 如果成功向chan2寫入數(shù)據(jù),則進(jìn)行該case處理語句
default:
// 如果上面都沒有成功缀台,則進(jìn)入default處理流程,如果沒有default流程則select會(huì)阻塞直到有一個(gè)case成功
}
sync包
Sync包同步提供基本的同步原語棠赛。
golang 并不推薦這個(gè)包中的大多數(shù)并發(fā)控制方法,但還是提供了相關(guān)方法膛腐,主要原因是golang中提倡以共享內(nèi)存的方式來通信睛约。
sync.Mutex
互斥鎖
var l sync.Mutex
l.Lock()
l.Unlock
sync.RWMutex
讀寫鎖
同時(shí)只能有一個(gè) goroutine 能夠獲得寫鎖定。
同時(shí)可以有任意多個(gè) gorouinte 獲得讀鎖定哲身。
同時(shí)只能存在寫鎖定或讀鎖定(讀和寫互斥)辩涝。
var l sync.RWMutex
l. RLock() // 加讀鎖
l. RUnlock() //加寫鎖
l. Lock()//加寫鎖
l. Unlock()//解寫鎖
sync. WaitGroup
WaitGroup可以用來等待一組goroutine結(jié)束
func (wg *WaitGroup) Add(delta int)//增加delta個(gè),注意勘天,wg.Add() 方法一定要在 goroutine 開始前執(zhí)行
func (wg *WaitGroup) Done() //計(jì)數(shù)-1
func (wg *WaitGroup) Wait() //等待結(jié)束
sync.Cond
條件變量
var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)
cond. Broadcast() // 喚醒所有掛起的goroutine
cond.Signal() //喚醒1個(gè)goroutine
cond.Wait() //必須要獲取鎖才能調(diào)用怔揩,Wait方法在調(diào)用時(shí)會(huì)釋放底層鎖Locker棍丐,并且將當(dāng)前goroutine掛起,直到另一個(gè)goroutine執(zhí)行Signal或者Broadcase沧踏,該goroutine才有機(jī)會(huì)重新喚醒歌逢,并嘗試獲取Locker,完成后續(xù)邏輯
sync.Once
保證只執(zhí)行一次
var once sync.Once
onceBody := func() {
fmt.Println("Only once")
}
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}