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 ...
随机推荐
- laravel 报错SQLSTATE[HY000] [2002] No such file or directory
在mac中执行php artisan migrate时报错 SQLSTATE[HY000] [2002] No such file or directory (SQL: select * from i ...
- Keil c中自定义带可变参数的printf函数
在嵌入式c中,往往采用串口打印函数来实现程序的调试,而在正式程序中一般是不需要这些打印代码的,通常做法是在这些调试用打印代码的前后设置一个宏定义块来实现是否启用这段代码,比如: // other us ...
- ELINK离线编程器常见问题
Q1 编程器是否可以接JTAG JTAG接口已经包含SWD接口引脚,按以下引脚对应接线即可: SWDIO->目标板JTAG 的JTMS SWCLK->目标板JTAG 的JTCK Q2 PC ...
- MS SQL SERVER搜索某个表的主键所在的列名
原文:MS SQL SERVER搜索某个表的主键所在的列名 SELECT SYSCOLUMNS.name FROM SYSCOLUMNS,SYSOBJECTS,SYSINDEXES,SYSINDEX ...
- 分配粒度和内存页面大小(x86处理器平台的分配粒度是64K,内存页是4K,所以section都是0x1000对齐,硬盘扇区大小是512字节,所以PE文件默认文件对齐是0x200)
分配粒度和内存页面大小 x86处理器平台的分配粒度是64K,32位CPU的内存页面大小是4K,64位是8K,保留内存地址空间总是要和分配粒度对齐.一个分配粒度里包含16个内存页面. 这是个概念,具体不 ...
- Android零基础入门第56节:翻转视图ViewFlipper打造引导页和轮播图
原文:Android零基础入门第56节:翻转视图ViewFlipper打造引导页和轮播图 前面两期学习了 ViewAnimator及其子类ViewSwitcher的使用,以及ViewSwitcher的 ...
- 简单图标转xaml代码
工具 PhotoShopCC(2017) Blend 2017 原图 步骤 1,使用钢笔工具勾勒出大致路径 2,将工作路径转为形状路径 3,图层右键->复制SVG <svg xmlns=& ...
- nodejs redis遇到的一个问题解决
v ar redis = require("redis"), client = redis.createClient({host:'tc-arch-osp33.tc', port: ...
- Windows完成端口与猪肉佬
首先应该说明的是,我也是第一次使用完成端口.虽然以前偶尔在网上看到完成端口的文章和代码,但真正自己动手写还是第一次,不过我这个人有个特点就是大胆,例如没有写那个界面编程系列前,其实我甚至不知道原来一个 ...
- Qt项目里的源代码默认都是Unicode,原因大概是因为qmake.conf里的定义
MAKEFILE_GENERATOR = MINGWQMAKE_PLATFORM = win32 mingwCONFIG += debug_and_release debug_and_release_ ...