Go 的并发 Concurrency
来源:互联网 发布:qq飞车神圣天使数据 编辑:程序博客网 时间:2024/05/24 00:49
Go 的并发
很多人都是冲着Go的高并发来学习Go的。而gorouting只是由官方实现的一个超级线程池而已,每个实例4-5KB的栈内存占用和由于实现机制而大幅减少创建和销毁的开销。这就是Go高并发的原因。
Go中并发程序依靠的是两个:goroutine和channel。
一个goroutine对新手来说可看作一个线程,但是他又不是一个线程。goroutine的出现正是为了替代原来的线程概念成为最小的调度单位。一旦运行goroutine时,先去当先线程查找,如果线程阻塞了,则被分配到空闲的线程,如果没有空闲的线程,那么就会新建一个线程。注意的是,当goroutine执行完毕后,线程不会回收推出,而是成为了空闲的线程。
Gorouting奉行通过通信来共享内存。而不是共享内存来通信。
一个最简单的gorouting实现。
package mainimport "fmt"func main(){go Go()}func Go(){fmt.Println("Go Go GO !!!")}只需要在调用函数前加一个 go 这个关键字就可以实现最简单的并发。但是执行结果试试。什么都没有输出。这是为什么??因为Go还没执行,main就执行结束了。所以没有打印。这时就需要一个chan了
那什么是chan呢?
channel:
--gorouting的沟通的桥梁。大都是阻塞同步的。
--通过make创建,close关闭。
--可以设置单向和双向通道,可以设置缓存大小,在未被填满之前不会发生阻塞。
下面简单的介绍通过chan阻塞的方式实现gorouting
package mainimport "fmt"func main(){c := make(chan bool)go func(){fmt.Println("Go Go Go !!!")c <- true}()<-c}
上面的创建了一个chan,内容存的是一个bool类型。在main函数的最后有一个<-c 。也就是c要往外扔一个数据,但c没数据可以扔啊,只有匿名函数里面的c传进去一个true这个bool型值时,main的最后才不会阻塞。
同样的道理,把'c <-' 和 c '<- ture' 调换个位置也可以,目的是阻塞他们呢。
chan是可以被迭代的。
package mainimport "fmt"func main(){c := make(chan bool)go func(){fmt.Println("Go Go Go !!!")c <- falseclose(c) //必须关闭他}()for v := range c {fmt.Println(v)}}main的最后在一直的迭代c。但是注意一定要close(c) 。要不然所有的gorouting都在等待,而变成了死锁。程序崩溃退出。
单向通道和双向通道。
make创建的chan是双向的,可存可取。
而单向的是只可以存,或者只可以写。
而缓存的。就是有空间的chan。比如缓存为10的通道,那么chan在没写满10的话,就可以一直写。
有缓存的话,在某些情况下是不阻塞的。
package mainimport "fmt"func main(){c := make(chan bool,1)go Go(c)<- c}func Go(c chan bool) {fmt.Println("Go Go Go !!!")c <- true}
注意,在make的最后设置缓存大小。这里是有缓存的,main的最后要扔出一个值,但没有值可扔。所以阻塞。
那把上述程序反过来。
package mainimport "fmt"func main(){c := make(chan bool,1)go Go(c)c <- true}func Go(c chan bool) {fmt.Println("Go Go Go !!!")<- c}这段程序翻过来了,main的最后要读入一个值,因为有缓存了,你想什么时候读就什么时候读,不读都可以,所以main这里的chan就起不到阻塞的作用了。
下面看这个例子
package mainimport "fmt"func main(){c := make(chan bool)for i := 0;i <10 ;i++ {go Go(c,i)}<- c}func Go(c chan bool,index int) {a := 1for i := 0; i < 10000000; i++ {a += i}fmt.Println(index, a)if index == 9 {c <- true}}
起10个gorouting。当10个循环都开始了才给 c 里面扔进去个true。让main的 c 终止阻塞。
但是看看输出
4 499999950000016 499999950000019 49999995000001
这是怎么回事??
问题一:首先每次都是按顺序去执行。前面的顺序永远是从小到大。这就不像是并发啊。
可以通过判断CPU核数的方法。
package mainimport ("fmt""runtime")func main(){runtime.GOMAXPROCS(runtime.NumCPU()) //判断CPU核数c := make(chan bool)for i := 0;i <10 ;i++ {go Go(c,i)}<- c}func Go(c chan bool,index int) {a := 1for i := 0; i < 10000000; i++ {a += i}fmt.Println(index, a)if index == 9 {c <- true}}执行结果:
3 499999950000016 499999950000017 499999950000014 499999950000018 499999950000019 49999995000001
问题二:输出明显不够10个啊
找个缓存为10的chan。读10次不久解决了吗
package mainimport ("fmt""runtime")func main(){runtime.GOMAXPROCS(runtime.NumCPU())c := make(chan bool,10)for i := 0;i <10 ;i++ {go Go(c,i)}for i := 0;i < 10;i++ {<-c}}func Go(c chan bool,index int) {a := 1for i := 0; i < 10000000; i++ {a += i}fmt.Println(index, a)c <- true}
看看输出
0 499999950000014 499999950000011 499999950000019 499999950000017 499999950000012 499999950000016 499999950000013 499999950000015 499999950000018 49999995000001
解决方法2:
sync。创建一个waitgroup . 创建10个任务。每完成一个任务,就会Done一下。main的最后在Wait。只有创建的10个任务全部都Done了,才会main退出结束。
package mainimport ("fmt""runtime""sync")func main(){runtime.GOMAXPROCS(runtime.NumCPU())wg := sync.WaitGroup{}wg.Add(10)for i := 0;i <10 ;i++ {go Go(&wg,i)}wg.Wait()}func Go(wg *sync.WaitGroup,index int) {a := 1for i := 0; i < 10000000; i++ {a += i}fmt.Println(index, a)wg.Done()}
select 呢
这是可以处理一个或者多个channel的发送与接收。
同时有多个可用的channel时按随机顺序处理。
可用空的select来阻塞main函数。
可设置超时。 其实和IO操作的select,poll,epoll那个select有点相似。
ch1 := make (chan int, 1)ch2 := make (chan int, 1)select {case <-ch1: fmt.Println("ch1 pop one element")case <-ch2: fmt.Println("ch2 pop one element")default: fmt.Println("default")}
还和switch有一点相似。
- Go 的并发 Concurrency
- Go并发concurrency
- Go 并发concurrency 学习笔记
- Go编程基础—并发(concurrency)
- GO语言的并发
- Go-简洁的并发
- Go-简洁的并发
- Go-简洁的并发
- Go-简洁的并发
- Go-简洁的并发
- Go的并发机制
- Go的并发
- golang: Golang 并发模式:超时和继续 Go Concurrency Patterns: Timing out, moving on
- Server -- Concurrency 服务端并发
- Java并发框架(Concurrency)
- [go]增加并发控制的并发ping
- [转]Go-简洁的并发
- go的并发机制goroutine
- C++知识总结(6)
- codeforces 838D D. Airplane Arrangements 构造法 推公式
- 数据结构实验之图论七:驴友计划
- 机器学习算法常用指标总结
- java 写一个可以给斗地主玩家随机发牌的程序。
- Go 的并发 Concurrency
- ffmpeg通过rtsp获取H264裸流并保存到mp4文件
- 个人介绍
- java中bio,nio,aio详解
- 1.11 C# 静态类和单例
- 虚函数作用
- APP测试流程梳理
- SQL NULL 值
- 一个仅接受两个输入向量的协方差计算函数