Golang 并发Groutine实例解读(一)
Go语言的并发和并行
不知道你有没有注意到一个现象,还是这段代码,如果我跑在两个goroutines里面的话:
var quit chan int = make(chan int)
func loop() {
for i := ; i < ; i++ {
fmt.Printf("%d ", i)
}
quit <-
}
func main() {
// 开两个goroutine跑函数loop, loop函数负责打印10个数
go loop()
go loop()
for i := ; i < ; i++ {
<- quit
}
}
我们观察下输出:
这是不是有什么问题??
以前我们用线程去做类似任务的时候,系统的线程会抢占式地输出, 表现出来的是乱序地输出。而goroutine为什么是这样输出的呢?
goroutine是在并行吗?
我们找个例子测试下:
package main import "fmt"
import "time" var quit chan int func foo(id int) {
fmt.Println(id)
time.Sleep(time.Second) // 停顿一秒
quit <- // 发消息:我执行完啦!
} func main() {
count :=
quit = make(chan int, count) // 缓冲1000个数据 for i := ; i < count; i++ { //开1000个goroutine
go foo(i)
} for i := ; i < count; i++ { // 等待所有完成消息发送完毕。
<- quit
}
}
让我们跑一下这个程序(之所以先编译再运行,是为了让程序跑的尽量快,测试结果更好):
go build test.go
time ./test
./test .01s user .01s system % cpu 1.016 total
我们看到,总计用时接近一秒。 貌似并行了!
我们需要首先考虑下什么是并发, 什么是并行
并行和并发
从概念上讲,并发和并行是不同的, 简单来说看这个图片

- 两个队列,一个Coffee机器,那是并发
- 两个队列,两个Coffee机器,那是并行
那么回到一开始的疑问上,从上面的两个例子执行后的表现来看,多个goroutine跑loop函数会挨个goroutine去进行,而sleep则是一起执行的。
这是为什么?
默认地, Go所有的goroutines只能在一个线程里跑 。
也就是说, 以上两个代码都不是并行的,但是都是是并发的。
如果当前goroutine不发生阻塞,它是不会让出CPU给其他goroutine的, 所以例子一中的输出会是一个一个goroutine进行的,而sleep函数则阻塞掉了 当前goroutine, 当前goroutine主动让其他goroutine执行, 所以形成了逻辑上的并行, 也就是并发。
真正的并行
为了达到真正的并行,我们需要告诉Go我们允许同时最多使用多个核。
回到起初的例子,我们设置最大开2个原生线程, 我们需要用到runtime包(runtime包是goroutine的调度器):
import (
"fmt"
"runtime"
) var quit chan int = make(chan int) func loop() {
for i := ; i < ; i++ { //为了观察,跑多些
fmt.Printf("%d ", i)
}
quit <-
} func main() {
runtime.GOMAXPROCS() // 最多使用2个核 go loop()
go loop() for i := ; i < ; i++ {
<- quit
}
}
这下会看到两个goroutine会抢占式地输出数据了。
我们还可以这样显式地让出CPU时间:
func loop() {
for i := ; i < ; i++ {
runtime.Gosched() // 显式地让出CPU时间给其他goroutine
fmt.Printf("%d ", i)
}
quit <-
}
func main() {
go loop()
go loop()
for i := ; i < ; i++ {
<- quit
}
}
观察下结果会看到这样有规律的输出:
其实,这种主动让出CPU时间的方式仍然是在单核里跑。但手工地切换goroutine导致了看上去的“并行”。
runtime调度器
runtime调度器是个很神奇的东西,但是我真是但愿它不存在,我希望显式调度能更为自然些,多核处理默认开启。
关于runtime包几个函数:
Gosched让出cpuNumCPU返回当前系统的CPU核数量GOMAXPROCS设置最大的可同时使用的CPU核数Goexit退出当前goroutine(但是defer语句会照常执行)
总结
我们从例子中可以看到,默认的, 所有goroutine会在一个原生线程里跑,也就是只使用了一个CPU核。
在同一个原生线程里,如果当前goroutine不发生阻塞,它是不会让出CPU时间给其他同线程的goroutines的,这是Go运行时对goroutine的调度,我们也可以使用runtime包来手工调度。
本文开头的两个例子都是限制在单核CPU里执行的,所有的goroutines跑在一个线程里面,分析如下:
- 对于代码例子一(loop函数的那个),每个goroutine没有发生堵塞(直到quit流入数据), 所以在quit之前每个goroutine不会主动让出CPU,也就发生了串行打印
- 对于代码例子二(time的那个),每个goroutine在sleep被调用的时候会阻塞,让出CPU, 所以例子二并发执行。
Golang 并发Groutine实例解读(一)的更多相关文章
- Golang 并发Groutine实例解读(二)
go提供了sync包和channel机制来解决协程间的同步与通信. 一.sync.WaitGroup sync包中的WaitGroup实现了一个类似任务队列的结构,你可以向队列中加入任务,任务完成后就 ...
- Golang 并发Groutine详解
概述 1.并行和并发 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行. 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在 ...
- 【WCF--初入江湖】08 并发与实例模式
08 并发与实例模式 1. 实例上下文模式 一个服务代理:servicePoxy ChannelFactory<IService1> factoryservicel = new Cha ...
- golang并发编程
golang并发编程 引子 golang提供了goroutine快速实现并发编程,在实际环境中,如果goroutine中的代码要消耗大量资源时(CPU.内存.带宽等),我们就需要对程序限速,以防止go ...
- 马蜂窝搜索基于 Golang 并发代理的一次架构升级
搜索业务是马蜂窝流量分发的重要入口.很多用户在使用马蜂窝时,都会有目的性地主动搜索与自己旅行需求相关的各种信息,衣食住行,事无巨细,从而做出最符合需求的旅行决策. 因此在马蜂窝,搜索业务交互的下游模块 ...
- Golang 并发简介
并发概要 随着多核CPU的普及, 为了更快的处理任务, 出现了各种并发编程的模型, 主要有以下几种: 模型名称 优点 缺点 多进程 简单, 隔离性好, 进程间几乎无影响 开销最大 多线程 目前使用最多 ...
- golang 并发顺序输出数字
参考 package main import ( "fmt" "sync/atomic" "time" ) func main() { va ...
- Python并发编程实例教程
有关Python中的并发编程实例,主要是对Threading模块的应用,文中自定义了一个Threading类库. 一.简介 我们将一个正在运行的程序称为进程.每个进程都有它自己的系统状态,包含内存状态 ...
- Golang并发原理及GPM调度策略(一)
其实从一开始了解到go的goroutine概念就应该想到,其实go应该就是在内核级线程的基础上做了一层逻辑上的虚拟线程(用户级线程)+ 线程调度系统,如此分析以后,goroutine也就不再那么神秘了 ...
随机推荐
- .Net应用程序 参照的组合没有安装在您的系统中。 HRESULT: 0x800736B3
同事打开一个.Net的应用程序链接,一直无法启动.错误信息为: 啓用xx.application 時發生例外狀況. 已偵測到下列失敗訊息:參照的組合沒有安裝在您的系統中. (發生例外狀況於 HRESU ...
- C# winform程序实现开机自启动,并且识别是开机启动还是双击启动
开机启动程序,在很多场合都会用到,尤其是那种在后台运行的程序. 效果图: 以上两幅图都用到了命令行启动程序,为了模拟开机启动或者其他程序调用此程序. 第一幅图:程序启动可以根据不同参数,执行不同的操作 ...
- The rapid development platform upgrade, leave the time to yourself, the work is lost to the soft platform
Bring me back to your home. Please leave your work behind! Soft agile development framework V7.0 new ...
- selenium下拉框踩坑埋坑
本文来自网易云社区 作者:王利蓉 最近web端全站重构,所有的页面都大大小小都有些变动,UI就全军覆没了,用例从登录改,改到个人信息页面发现根以前的实现方式完全不一样,这可怎么解决 1.以前的实现(o ...
- LinkedBlockingQueue源码解析(3)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.3.public E take() throws InterruptedException 原理: 将队 ...
- celery_消息队列
http://www.cnblogs.com/wupeiqi/articles/8796552.html 一. celery 简介 Celery 是一个专注于实时处理和任务调度的分布式任务队列, 同时 ...
- map函数和reduce函数、filter函数的区别
①从参数方面来讲:map()函数: map()包含两个参数,第一个是参数是一个函数,第二个是序列(列表或元组).其中,函数(即map的第一个参数位置的函数)可以接收一个或多个参数.reduce()函数 ...
- git生成Key操作保存到GITHUB中
https://blog.csdn.net/sinat_23880167/article/details/78502528 1. 在git中通过命令: $ ssh-keygen Generating ...
- FFmpeg音视频解封装
一 . 解封装用到的函数和结构体 1.av_register_all() : open 一次就调用一次 2.avformat_network_init() : 网络模块初始化 3.avformat_o ...
- 801. Minimum Swaps To Make Sequences Increasing
We have two integer sequences A and B of the same non-zero length. We are allowed to swap elements A ...