go语言goroutine
Go语言goroutine
在别的语言里想要在一个程序中实现多任务,如python,python实现多任务可以使用多进程、多线程、携程。但多进程占用资源,多线程无法发挥多核的优势(GIL),python的协程是单线程的,必须等一个任务作出让步,另一个任务才能执行,如果其中一个任务阻塞住,让不出cpu来,那么整个程序都会被阻塞住。
go语言的goroutine(协程)是一个类似于线程的概念,但是它比线程轻量。当开启了多个goroutine后,程序会将每个goroutine分配给每个cpu的核心,可以充分发挥cpu多核的效率。go的协程效率比为m(cpu核数)/n(goroutine数),而python的协程的效率比为1/n
开启goroutine
只需要在将任务封装成一个函数,使用go关键字,就可以开启一个goroutine完成多任务。同时,程序的主函数也是一个goroutine
package main
import "fmt"
func work() {
fmt.Printf("work goroutine")
}
func main() {
go work()
fmt.Println("main goroutine")
}
sync.WaitGroup
上面的程序启动goroutine是没问题,但是编译运行后会发现只有打印了main goroutine。这是因为,开启在主goroutine里面启动另外一个goroutine后,另外开启的goroutine还没来得及运行,主goroutine就已经结束了,主goroutine结束,由主goroutine中开启的goroutine全部都会结束,所有主goroutine想要其他的goroutine执行,必须得等待。
package main
import (
"fmt"
"time"
)
func work() {
fmt.Println("work")
}
func main() {
go work()
fmt.Println("main goroutine")
time.Sleep(time.Second)
}
使用time.Sleep来是程序休眠,来达到让另外goroutine由充足的时间来运行
但是当开启了多个goroutine后,我们不知道全部的协程运行完毕需要多少时间,我们就无法估量sleep的时间。sleep的时间可能多了,可能少了,少了就会让能成达不到想要的目的,多了就会让程序多休眠,浪费资源,此时,就需要引入sync模块的工具来解决这个问题
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func work() {
defer wg.Done()
fmt.Println("work")
}
func main() {
wg.Add(1)
go work()
fmt.Println("main goroutine")
wg.Wait() // 等待所有的协程能全部运行完毕
}
使用sync.WaitGroup,当需要开启一个协程前,往WaitGroup里面加1,在协程结束后,向WaitGroup告知已经完成(可以配合defer 关剪字使用),最后在主goroutine(main函数)的最后使用WaitGroup来等待所有的goroutine运行结束(对应的函数执行结束)
goroutine和线程的关系
这里的线程指的是操作系统的线程,线程和goroutine启动占用的资源不一样,线程的栈内存一般为2mb,而goroutine在开始的时候占用的栈内存为2kb,goroutine的栈不是固定的,它可以像go的切片一样扩容,最大限制可以达到一个G,所有在go里面可以很容易开启十万个goroutine。
- GMP调度
- G:G代表goroutine,里面存放当前的goroutine的信息和当前goroutine所载的P的绑定等信息
- M:M是go运行时对操作系统的线程的虚拟。一个goroutine最终都是要放在M上运行的
- P:P管理者一组goroutine队列,P里面存放着当前的goroutine的上下文,P会对当前管理的goroutine队列做一些调度(某些goroutine可能占用cpu时间过长,P会将这个goroutine暂停,然后去让别的goroutine来执行)。当自己的goroutine全部运行结束了,这个P会去全局的队列里面取,如果全局的里面也没有了,它甚至会去别的P里面取获取goroutine,这样可以实现效率的最大化
- P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。
go语言goroutine的更多相关文章
- TODO:Go语言goroutine和channel使用
TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...
- Go语言goroutine调度器初始化(12)
本文是<Go语言调度器源代码情景分析>系列的第12篇,也是第二章的第2小节. 本章将以下面这个简单的Hello World程序为例,通过跟踪其从启动到退出这一完整的运行流程来分析Go语言调 ...
- Go语言goroutine调度器概述(11)
本文是<go调度器源代码情景分析>系列的第11篇,也是第二章的第1小节. goroutine简介 goroutine是Go语言实现的用户态线程,主要用来解决操作系统线程太“重”的问题,所谓 ...
- Go语言 - goroutine
并发与并行 并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天). 并行:同一时刻执行多个任务(你和你朋友都在用微信和女朋友聊天). Go语言的并发通过goroutine实现.goroutin ...
- Go语言 | goroutine不只有基础的用法,还有这些你不知道的操作
今天是golang专题第15篇文章,我们来继续聊聊channel的使用. 在我们的上篇文章当中我们简单介绍了golang当中channel的使用方法,channel是golang当中一个非常重要的设计 ...
- Go语言学习之8 goroutine详解、定时器与单元测试
主要内容: 1.Goroutine2. Chanel3. 单元测试 1. Goroutine Go 协程(Goroutine)(轻量级的线程,开线程没有数量限制). (1)进程和线程 A. 进程是 ...
- 09. Go 语言并发
Go 语言并发 并发指在同一时间内可以执行多个任务.并发编程含义比较广泛,包含多线程编程.多进程编程及分布式程序等.本章讲解的并发含义属于多线程编程. Go 语言通过编译器运行时(runtime),从 ...
- 分布式 基本理论 BASE
比起CAP的生硬来, 我更加喜欢BASE.BASE 显得更加好理解.尽管BASE 这个名字本身有迎合语言习惯的恶臭味,为什么不是缩写为 BaSsEc 呢? 其实大部分系统是可以 同时 CAP 的, 因 ...
- golang 学习笔记 ---内存分配与管理
Go语言——内存管理 参考: 图解 TCMalloc Golang 内存管理 Go 内存管理 问题 内存碎片:避免内存碎片,提高内存利用率. 多线程:稳定性,效率问题. 内存分配 内存划分 are ...
随机推荐
- css中:overflow:hidden清除浮动的原理
要想彻底清除浮动的影响,适合的属性不是 clear 而是 overflow. 一般使用 overflow:hidden,利用 BFC 的“结界”特性彻底解决浮动对外部或兄弟元素的影响. 1. 前言: ...
- 【Python】pyinstaller打包运行报错failed to execute script main
前言 最近用pyinstaller打包的时候一直报"failed to execute script main". 最终使用"pyinstaller --hidden-i ...
- Scala尾递归
递归函数应用 首先,我们来对比两个递归方法的求值步骤. 假设有方法gcd,用来计算两个数的最大公约数.下面是欧几里得算法的实现: def gcp(a: Int, b: Int): Int = if ( ...
- JDK14发布了,快来看看有什么新特性吧
序言
- 998. 最大二叉树 II
最大树定义:一个树,其中每个节点的值都大于其子树中的任何其他值. 给出最大树的根节点 root. 就像之前的问题那样,给定的树是从表 A(root = Construct(A))递归地使用下述 Con ...
- Python基础 | 日期时间操作
目录 获取时间 时间映射 格式转换 字符串转日期 日期转字符串 unixtime 时间计算 时间偏移 时间差 "日期时间数据"作为三大基础数据类型之一,在数据分析中会经常遇到. 本 ...
- [C++]请麻烦压一下定理的棺材板啦
从去年还在竞赛的时候2/12的原博客里搬运来的 不得不说之前取名真的很艺术qwq 今天开始上的数论课,让头发以肉眼可见的速度掉落emmm 没关系我头发多我不怕啦啦啦QwQ 其中最令人头疼的就是那些人名 ...
- docker系列详解<一>之docker安装
1.Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的CentOS 版本是否支持 Docker . 通过 uname -r 命令查看你当前的内核版本 $ ...
- ASP.NET Core - 在ActionFilter中使用依赖注入
上次ActionFilter引发的一个EF异常,本质上是对Core版本的ActionFilter的知识掌握不够牢固造成的,所以花了点时间仔细阅读了微软的官方文档.发现除了IActionFilter.I ...
- 遍历集合的常见方式,排序,用lambda表示是怎样的
Collection集合的功能: Object[] toArray() 将集合转成数组 Iterator iterator() 通过方法的调用 获取I ...