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 Layout 系统概述——Arrange
原文:WPF Layout 系统概述--Arrange Arrange过程概述 普通基类属性对Arrange过程的影响 我们知道Measure过程是在确定DesiredSize的大小,以便Arrang ...
- Rxjava 学习(一)
Rxjava是什么? RxJava是由Netflix开发的响应式扩展(Reactive Extensions)的Java实现.引用MSDN上对它的定义,Reactive Extensions是这样一个 ...
- google的开源项目总结(转载)
转自http://www.feng5166.com/blog/424.html google的开源项目值得我们一用的,这些项目很有意义,甚至可以直接用在我们自己的工作上!学习编程的的一个比较好的方式就 ...
- 【Ubuntu】查看系统资源占用(内存,cpu和进程)
1 top 查看ubuntu的资源占用的命令为 $: top 说明:top命令就可以查看内存,cpu和进程了,很方便 top: 主要参数: d:指定更新的间隔,以秒计算. q:没有任何延迟的更新.如果 ...
- adb help
Android Debug Bridge version 1.0.32Revision cc6ae925e394-android -a - directs adb to listen on all i ...
- Qt之自定义搜索框——QLineEdit里增加一个Layout,还不影响正常输入文字(好像是一种比较通吃的方法)
简述 关于搜索框,大家都经常接触.例如:浏览器搜索.Windows资源管理器搜索等. 当然,这些对于Qt实现来说毫无压力,只要思路清晰,分分钟搞定. 方案一:调用QLineEdit现有接口 void ...
- LINQ查询表达式---------join子句
LINQ查询表达式---------join子句 join 子句接受两个源序列作为输入. 每个序列中的元素都必须是可以与另一个序列中的相应属性进行比较的属性,或者包含一个这样的属性. join子句使用 ...
- UWP开发-获取设备唯一ID
EasClientDeviceInformation deviceInfo = new EasClientDeviceInformation(); this.showDeviceInfo.Items. ...
- Delphi的对象注销方法Destroy和free
当您使用完对象后,您应该及时撤销它,以便把这个对象占用的内存释放出来.您可以通过调用一个注销方法来撤销您的对象,它会释放分配给这个对象的内存. Delphi的注销方法有两个:Destroy和Free. ...
- autotools工具使用 good
学习GNU/LINUX开发的编程人员,上手之后不久就会在编译开源软件的时候碰到configure脚本,过段时间还会知道configure脚本是 autoconf生成的:但是真正想用起来autoconf ...