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也就不再那么神秘了 ...
随机推荐
- bootstrap-table 中取主键字段的问题,主键名不叫id
问题 :取不到数据行的主键 要绑定的数据字段 RoleId rolename adddate RoleId 为主键是唯一的 bootstraptable的配置 uniqueId: "Role ...
- NetCore入门篇:(七)Net Core项目使用Controller之二
一.简介 1.说明Post,Get定义的区别. 2.说明如何路由定义. 二.Get.Post定义 1.api不定义访问方式时,同时支持get 和 post.如果定义某种方式,则仅支持某种方式.具体看代 ...
- max渲染通道元素的范例
renderElementManager = MaxOps.GetCurRenderElementMgr() renderElementManager.Removeallrenderelements( ...
- leetcode 91. 解码方法 JAVA
题目: 一条包含字母 A-Z 的消息通过以下方式进行了编码: 'A' -> 1 'B' -> 2 ... 'Z' -> 26 给定一个只包含数字的非空字符串,请计算解码方法的总数. ...
- 协程IO多路复用
协程:单线程下实现并发并发:伪并行,遇到IO就切换,单核下多个任务之间切换执行,给你的效果就是貌似你的几个程序在同时执行.提高效率任务切换 + 保存状态并行:多核cpu,真正的同时执行串行:一个任务执 ...
- Smarty的原理_面试
Smarty是一个模板引擎,使用smarty主要是为了实现逻辑和外在内容的分离,如果不使用模板的话,通常的做法就是php代码和html代码混编.使用了模板后,则可以将业务逻辑放到php文件中,而负责显 ...
- [Swift实际操作]七、常见概念-(1).范围Range、ClosedRange和NSRange的使用实际操作
本文将为你解析区间的概念.在对字符串进行处理时,经常会使用到区间.例如获得字符串中从某个位置开始到另一个位置之间的内容. 首先导入需要使用到的框架 import Foundation 区间分为闭区间和 ...
- Tcp下载文件
一.下载文件 tcp 客户端 1.创建套接字down_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)2.获取ip,portdown ...
- python基础知识input到while循环
j周笔记 输入与输出 1.输入 input ('请输入内容')= 字符串 2.输出 print(输出到控制台) 变量vairable 变量就是相当于我们人的名字 1.名字 ...
- Memcached 查看列出所有key方法
Memcached没有一个比较简单的方法可以直接象Redis那样keys *列出所有的Session key,并根据key get对应的session内容,但是还是可以查看的 memcached 查看 ...