Go协程并发之百万级并发「让我们一起Golang」
前面我们介绍了关于Golang的协程并发的一些理论知识,接下来我们上代码,通过代码带大家了解一下Go的协程并发,体验Go的百万级并发。
我们先来了解一下进程、线程、协程的区别吧!
进程有自己独立的堆和栈,而线程虽然拥有独立的栈,但是它的堆是共享的。而我们这里要讲的协程和线程是一样的,也是只共享堆,而不共享栈。但是协程不像进程和线程那样由操作系统调度,而是由程序员来调度,这样就可以节省资源,提高性能。
下面来看看主协程和一条协程在并发执行时的表现吧!
func main() { go func() { for{ fmt.Println("i am coroutine") time.Sleep(time.Second) } }() for{ fmt.Println("我是主协程") time.Sleep(time.Second) } }
他们运行结果是:
我是主协程 i am coroutine i am coroutine 我是主协程 我是主协程 i am coroutine i am coroutine 我是主协程 i am coroutine 我是主协程
我们可以看到,他们并不是顺序执行的,而是并发执行的,这就是协程并发。
func main() { //但是开启100条小协程是主协程干的 //迅速开启100条小协程 for i:=0;i<10;i++{ go doSomething("面包人"+strconv.Itoa(i)) } for{ fmt.Println("我是主协程") time.Sleep(time.Second) } } func doSomething(grname string) { for{ fmt.Println("来了一车",grname) time.Sleep(time.Second) } }
来了一车 面包人0 来了一车 面包人7 来了一车 面包人8 我是主协程 来了一车 面包人2 来了一车 面包人6 来了一车 面包人3 来了一车 面包人1 来了一车 面包人5 来了一车 面包人9 来了一车 面包人4 来了一车 面包人0 来了一车 面包人8 来了一车 面包人1 来了一车 面包人2 来了一车 面包人4 来了一车 面包人7 来了一车 面包人3 来了一车 面包人9 来了一车 面包人5 我是主协程 来了一车 面包人6 来了一车 面包人1 来了一车 面包人4
这里体验的是11条协程并发。是主协程唤起了十条协程,然后才有一车车面包人来打你。因此主协程是那个打电话喊面包人打你的人。也就是说主协程开启了10条协程。但是这十一条协程是并发的,他们不是顺序执行的。虽然是并发执行的,但是在微观上每次还是只能执行一条协程,但是他们的执行顺序和创建顺序是不一致的。
然后将
for i:=0;i<10;i++{ go doSomething("面包人"+strconv.Itoa(i)) }
改为
for i:=0;i<1000000;i++{ go doSomething("面包人"+strconv.Itoa(i)) }
这就是百万级并发了!
一个主协程可以喊100万车面包人打你。
经过测试,程序持续1分钟都没有崩溃。事实证明,执行协程只需要消耗极少的内存和CPU资源,所以我们可以创建一百万条协程。16G内存的电脑,用JAVA,C来做并发,差不多也就千级并发,而用GO语言,通过管道可以让并发能力得到很大提升,我们这里实现了百万级并发。
goroutine是协作式调度的,而Java等的使用的线程是抢占式调度的。
Go 任务调度