并发 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")
}
}
网友评论