并發(fā) concurrency
- Goroutine 通過(guò)通信來(lái)共享內(nèi)存,而不是通過(guò)共享內(nèi)存來(lái)通信
- Channel是Goroutine的溝通橋梁,引用類型,大都是阻塞同步的
- make創(chuàng)建,close關(guān)閉
- for range迭代操作channel,可以設(shè)置單向或者雙向管道,緩存大小,為填滿之前不會(huì)堵塞
- Select
- 可以處理一個(gè)或者多個(gè)channel的發(fā)送和接收
- 同時(shí)有多個(gè)可以用的chnnel隨機(jī)順序處理
- 可設(shè)置空的select阻塞main函數(shù),可設(shè)置超時(shí)
- 單向管道 chan<-int只發(fā)送同道,<-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異步處理循環(huán)打印
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//設(shè)置chan值
}
func goChan() {//達(dá)到異步執(zhí)行
runtime.GOMAXPROCS(1)/設(shè)置cpu最大可用核數(shù)
c := make(chan bool,10)//設(shè)置緩存大小,有緩存的時(shí)候是異步的,無(wú)緩存是同步堵塞
for i := 0; i < 10; i++ {
go my.ChanRange(i,c)//開啟一個(gè)新的goroutine執(zhí)行
}
for i:=0;i<10;i++{
<-c//取出chan值
}
}
sync異步循環(huán)打印
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()//執(zhí)行完畢一個(gè),線程池就會(huì)減少一個(gè)線程
}
func goSync() {//達(dá)到異步執(zhí)行
//runtime.GOMAXPROCS(runtime.NumCPU())/設(shè)置cpu最大可用核數(shù)
wg:=sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go my.SyncRange(i,&wg)
}
wg.Wait()
}
并發(fā)不是并行,==老的版本默認(rèn)==,Go所有的goroutines只能在一個(gè)線程里跑,也就是說(shuō),設(shè)置核數(shù)為1地,goChan方法不是并行的律杠,是并發(fā)的块差,==新版本默認(rèn)==把runtime.GOMAXPROCS(runtime.NumCPU())設(shè)置最大的,那么他就是并行的
- 兩個(gè)隊(duì)列,一個(gè)Coffee機(jī)器燎潮,那是并發(fā)
- 兩個(gè)隊(duì)列喻鳄,兩個(gè)Coffee機(jī)器,那是并行
package main
import (
"fmt"
)
func say(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func main() {
go say("world") //開一個(gè)新的Goroutines執(zhí)行
for {
}
}
這里Go仍然在使用單核确封,for死循環(huán)占據(jù)了單核CPU所有的資源除呵,而main線和say兩個(gè)goroutine都在一個(gè)線程里面,所以say沒(méi)有機(jī)會(huì)執(zhí)行
允許Go使用多核(runtime.GOMAXPROCS)
手動(dòng)顯式調(diào)動(dòng)runtime包(runtime包是goroutine的調(diào)度器),
- Gosched 讓出cpu
- NumCPU 返回當(dāng)前系統(tǒng)的CPU核數(shù)量
- GOMAXPROCS 設(shè)置最大的可同時(shí)使用的CPU核數(shù)
- Goexit 退出當(dāng)前goroutine(但是defer語(yǔ)句會(huì)照常執(zhí)行)
func loop() {
for i := 0; i < 10; i++ {
runtime.Gosched() // 顯式地讓出CPU時(shí)間給其他goroutine
fmt.Printf("%d ", i)
}
quit <- 0
}
func main() {
go loop()
go loop()
for i := 0; i < 2; i++ {
<- quit
}
}
- 上面代碼這種主動(dòng)讓出CPU時(shí)間的方式仍然是在單核里跑爪喘。但手工地切換goroutine導(dǎo)致了看上去的“并行”颜曾,stackoverflow的 解釋:https://stackoverflow.com/questions/13107958/what-exactly-does-runtime-gosched-do
當(dāng)一個(gè)goroutine發(fā)生阻塞,Go會(huì)自動(dòng)地把與該goroutine處于同一系統(tǒng)線程的其他goroutines轉(zhuǎn)移到另一個(gè)系統(tǒng)線程上去秉剑,以使這些goroutines不阻塞,也就是說(shuō),goroutine不阻塞不放開CPU
package main
import (
"fmt"
"runtime"
)
var quit chan int = make(chan int)
func loop(id int) { // id: 該goroutine的標(biāo)號(hào)
for i := 0; i < 10; i++ { //打印10次該goroutine的標(biāo)號(hào)
fmt.Printf("%d ", id)
}
quit <- 0
}
func main() {
runtime.GOMAXPROCS(2) // 最多同時(shí)使用2個(gè)核
for i := 0; i < 3; i++ { //開三個(gè)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)
}
- 設(shè)置超時(shí)處理
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")
}
}