goroutine上下文切换机制
goroutine是go语言的协程,go语言在语言和编译器层面提供对协程的支持。goroutine跟线程一个很大区别就是线程是操作系统的对象,而goroutine是应用层实现的线程。goroutine实际上是运行在线程池上的,由go的runtime实现调度,goroutine调度时,由于不需要像线程一样涉及到系统调用,要进行用户态和内核态的切换,因此,goroutine被称为轻量级的线程,开销要比线程小很多。然而,这里我想到了一个问题,线程是由操作系统进行调度的,操作系统有对处理器的调度权限,因此线程在上下文切换时,操作系统可以从正在占用处理器的线程手中剥夺处理器的使用权,然而goroutine该怎么完成这个操作呢?
然而goroutine并不能像线程的调度那样,goroutine调度时,必须由当前正在占用CPU的goroutine主动让出CPU给新的goroutine,才能完成切换操作。
具体实现是这样的,go对所有的系统调用进行了封装,当前执行的goroutine如果正在执行系统调用或者可能会导致当前goroutine阻塞的操作时,runtime就会把当前这个goroutine切换掉。因此一个很有意思的事情就发生了,如果当前的goroutine没有出现上述的可能会导致goroutine切换的条件时,就可以一直占用CPU(实际上只是一直占用线程),而且并不会因为这个goroutine占用时间太长而进行切换。我们可以通过如下这段代码进行验证:
package main import (
"fmt"
"sync"
) func process(id int) {
fmt.Printf("id: %d\n", id)
for {
}
}
func main() {
var wg sync.WaitGroup
n :=
wg.Add(n)
for i := ; i < n; i++ {
go process(i)
}
wg.Wait()
}
这段代码输出如下:
id: 9
id: 5
id: 6
id: 0
按照正常的逻辑,这段代码应该会输出0到9一共十个id,然而执行后发现,只输出了四个(GOMAXPROCS: goroutine底层线程池最大线程数,默认为硬件线程数)id,这就说明实际只有四个goroutine得到了CPU,而且没有进行切换,因为process这个方法里面没有会导致goroutine切换的条件。然后我们在for循环里面加入一个操作,例如time.Sleep()或者make分配内存等等
package main import (
"fmt"
"sync"
"time"
) func process(id int) {
fmt.Printf("id: %d\n", id)
for {
time.Sleep(time.Second)
}
}
func main() {
var wg sync.WaitGroup
n :=
wg.Add(n)
for i := ; i < n; i++ {
go process(i)
}
wg.Wait()
}
Output:
id: 2
id: 0
id: 1
id: 9
id: 6
id: 3
id: 7
id: 8
id: 5
id: 4
可以看到这次的输出就是我们预料的结果了。在知道goroutine的调度策略之后,可以想到这种策略可能会带来的问题,假如有n个goroutine出现阻塞,并且n >= GOMAXPROCS时,将会导致整个程序阻塞。
然而这个问题是无法从根本上解决的,所以go给我们提供了一个方法runtime.Gosched(),调用这个方法可以让当前的goroutine主动让出CPU,这也不失为一个弥补的好方法了。而且在对go程序性能调优的时候,我们可以根据实际情况来调整GOMAXPROCS的值,例如当有密集的IO操作时,尽量把这个值设置大一点,可以避免由于大量IO操作导致阻塞线程。
以上内容纯属原创,如有问题欢迎指正!
goroutine上下文切换机制的更多相关文章
- [转]golang的goroutine调度机制
golang的goroutine调度机制 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 一直对goroutine的调度机制很好奇最近在看雨痕的golang源码分析基于go ...
- Golang(三)Goroutine原理
前言 最近用到了一些 Golang 异步编程的地方,感觉 Golang 相对于其他语言(如 Java)对多线程编程的支持非常大,使用起来也非常方便.于是决定了解一下 Goroutine 的底层原理. ...
- fasthttp 的 goroutine pool 实现探究
引言 fasthttp是一个非常优秀的web server框架,号称比官方的net/http快10倍以上.fasthttp用了很多黑魔法.俗话说,源码面前,了无秘密,我们今天通过源码来看一看她的gor ...
- 理解Go Context机制
1 什么是Context 最近在公司分析gRPC源码,proto文件生成的代码,接口函数第一个参数统一是ctx context.Context接口,公司不少同事都不了解这样设计的出发点是什么,其实我也 ...
- Go 并发操作
goroutine 在其他的编程语言中,线程调度是交由os来进行处理的. 但是在Go语言中,会对此做一层封装,Go语言中的并发由goroutine来实现,它类似于用户态的线程,更类似于其他语言中的协程 ...
- [CSAPP笔记][第八章异常控制流][呕心沥血千行笔记]
异常控制流 控制转移 控制流 系统必须能对系统状态的变化做出反应,这些系统状态不是被内部程序变量捕获,也不一定和程序的执行相关. 现代系统通过使控制流 发生突变对这些情况做出反应.我们称这种突变为异常 ...
- 处理器(CPU)调度问题
因为处理器是最重要的计算机资源,提高利用率并提高系统性能的处理器(吞吐量.响应时间).于处理机调度性能的好坏,因而,处理机调度便成为操作系统设计的中心问题之中的一个. 一.处理机调度的层次 1. ...
- Go学习笔记02-源码
第二部分 源码 基于 Go 1.4,相关文件位于 src/runtime 目录.文章忽略了 32bit 代码,有兴趣的可自行查看源码文件.为便于阅读,示例代码做过裁剪. 1. Memory Alloc ...
- Go学习笔记01-语言
1.1 变量 Go 是静态类型语言,不能在运行期改变变量类型.使用关键字 var 定义变量,自动初始化为零值.如果提供初始化值,可省略变量类型,由编译器自动推断. var x int var f fl ...
随机推荐
- WPF DataGrid支持的列类型
WPF DataGrid支持下面几种列类型: DataGridTextColumn DataGridCheckBoxColumn DataGridComboBoxColumn DataGridHype ...
- 检查Android是否支持指纹识别以及是否已经录入指纹
原文:检查Android是否支持指纹识别以及是否已经录入指纹 Android M 开始,系统中加入了指纹相关功能. 主要用到的类为:FingerprintManager 只提供三个方法: 返回值 方法 ...
- TCP 的那些事儿(上,下)
http://coolshell.cn/articles/11564.html http://coolshell.cn/articles/11609.html
- linux 修改 ssh 的端口号,启动hg服务
修改 ssh 的端口号 # vim /etc/ssh/sshd_config 将其中的 Port 改为 想要修改的端口号. # service sshd restart 启动hg服务 # hg ...
- C#可扩展编程之MEF学习笔记(五):MEF高级进阶(转)
好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...
- 发布ActiveX控件
最近我们正在研究ActiveX技术.我们使用Delphi 5创建了一个具有ActiveForm的ActiveX控件应用程序.这个控件产生一个.OCX文件.现在,我们需要把这个控件部署在服务器端,在用户 ...
- UWP 发送短信公用倒计时按钮
1.要求: 发送验证码按钮,点击后,会倒计时60s,之后才能再次点击.不同界面的多个验证码按钮共享这个倒计时时间. 2.操作步骤 1) 从登录界面-->忘记密码输入手机号- ...
- c# 查询本机可用的代理ip
现在有很多网站都提供免费的代理ip,但是你会发现很多网站显示的可以用的 ,在自己电脑上是用不了,写个小代码提取出自己电脑上可以用的代理,没什么技术含量,只是为了记录一下 string strUrl = ...
- Ansible的安装与使用初探
一.环境准备 网络配置 管理端:192.168.237.201 受控端:192.168.237.202.192.168.237.203(一共2台) 硬件信息 CPU:1核 内存:512MB 磁盘:10 ...
- Win7 访问 数据库 慢
不让TCP/IP调谐拖累网速 在Windows Server 2008工作环境中,下载访问网络中大容量的文件内容时,我们有时会感觉到网络连接速度非常缓慢,严重的时候还会出现不能访问的现象.遭遇这类故障 ...