我们在使用Go语言进行开发时,一般会使用goroutine来处理并发任务。那么大家有没有考虑过goroutine的实现机制是什么样的?很多同学会把goroutine与线程等同起来,但是实际上并不是这样的。在这边文章中,我们将介绍以下内容:

  • 什么是goroutine?
  • Goroutine与线程的区别
  • Goroutine是如何调度的

1. 什么是goroutine?

Goroutine是建立在线程之上的轻量级的抽象。它允许我们以非常低的代价在同一个地址空间中并行地执行多个函数或者方法。相比于线程,它的创建和销毁的代价要小很多,并且它的调度是独立于线程的。在golang中创建一个goroutine非常简单,使用“go”关键字即可:

package main
import ( "fmt"    "time"
)func learning() { fmt.Println("My first goroutine")
}func main() { go learning() /* we are using time sleep so that the main program does not terminate before the execution of goroutine.*/ time.Sleep(1 * time.Second) fmt.Println("main function")
}

这段代码的输出是这样的:

My first goroutinemain function

如果把Sleep去掉的话,输出就会变成:

main function

这是因为,和线程一样,golang的主函数(其实也跑在一个goroutine中)并不会等待其它goroutine结束。如果主goroutine结束了,所有其它goroutine都将结束。

2. Goroutine与线程的区别

许多人认为goroutine比线程运行得更快,这是一个误解。Goroutine并不会更快,它只是增加了更多的并发性。当一个goroutine被阻塞(比如等待IO),golang的scheduler会调度其它可以执行的goroutine运行。与线程相比,它有以下几个优点:

  • 内存消耗更少
    Goroutine所需要的内存通常只有2kb,而线程则需要1Mb(500倍)。
  • 创建与销毁的开销更小
    由于线程创建时需要向操作系统申请资源,并且在销毁时将资源归还,因此它的创建和销毁的开销比较大。相比之下,goroutine的创建和销毁是由go语言在运行时自己管理的,因此开销更低。
  • 切换开销更小
    这是goroutine于线程的主要区别,也是golang能够实现高并发的主要原因。线程的调度方式是抢占式的,如果一个线程的执行时间超过了分配给它的时间片,就会被其它可执行的线程抢占。在线程切换的过程中需要保存/恢复所有的寄存器信息,比如16个通用寄存器,PC(Program Counter),SP(Stack Pointer),段寄存器等等。
    而goroutine的调度是协同式的,它不会直接地与操作系统内核打交道。当goroutine进行切换的时候,之后很少量的寄存器需要保存和恢复(PC和SP)。因此gouroutine的切换效率更高。

3.Goroutine的调度

真如前面提到的,goroutine的调度方式是协同式的。在协同式调度中,没有时间片的概念。为了并行执行goroutine,调度器会在以下几个时间点对其进行切换:

  • Channel接受或者发送会造成阻塞的消息
  • 当一个新的goroutine被创建时
  • 可以造成阻塞的系统调用,如文件、网络操作及Sleep等
  • 垃圾回收

下面让我们来看一下调度器具体是如何工作的。Golang调度器中有三个概念

  • Processor(P)
  • OSThread(M)
  • Goroutines(G)

在一个Go程序中,可用的线程数是通过GOMAXPROCS来设置的,默认值是可用的CPU核数。我们可以用runtime包动态改变这个值。OSThread调度在processor上,goroutines调度在OSThreads上,如下图所示


Golang的调度器可以利用多processor资源,在任意时刻,M个goroutine需要被调度到N个OS threads上,同时这些threads运行在至多GOMAXPROCS个processor上(N <= GOMAXPROCS)。Go scheduler将可运行的goroutines分配到多个运行在一个或多个processor上的OS threads上。

每个processor有一个本地goroutine队列。同时有一个全局的goroutine队列。每个OSThread都会被分配给一个processor。最多只能有GOMAXPROCS个processor,每个processor同时只能执行一个OSThread。Scheculer可以根据需要创建OSThread。

在每一轮调度中,scheduler找到一个可以运行的goroutine并执行直到其被阻塞。

Search in the local queueif not found Try to steal from other Ps’ local queue //see Fig 3 if not found Search in the global queue Also periodically it searches in the global queue (every ~ 1/70)

由此可见,操作系统的一个线程下可以并发执行上千个goroutine,每个goroutine所占用的资源和切换开销都很小,因此,goroutine是golang适合高并发场景的重要原因。

Golang中Goroutine与线程相关推荐

  1. [转]Golang中goroutine的调度器详解

    Go调度器原理浅析 来源:https://www.douban.com/note/300631999/ goroutine是golang的一大特色,或者可以说是最大的特色吧(据我了解),这篇文章主要翻 ...

  2. Golang中WaitGroup、Context、goroutine定时器及超时学习笔记

    原文连接:http://targetliu.com/2017/5/2... 好久没有发过文章了 - -||,今天发一篇 golang 中 goroutine 相关的学习笔记吧,以示例为主. WaitG ...

  3. Golang中Buffer高效拼接字符串以及自定义线程安全Buffer

    本文原创文章,转载注明出处,博客地址 https://segmentfault.com/u/to... 第一时间看后续精彩文章.觉得好的话,顺手分享到朋友圈吧,感谢支持. Go中可以使用"+ ...

  4. php协程和goroutine,golang中四种方式实现子goroutine与主协程的同步

    如何实现子goroutine与主线程的同步 第一种方式:time.sleep(),这种方式很太死板,就不演示了. 第二种方式:使用channel机制,每个goroutine传一个channel进去然后 ...

  5. Golang 中的 Goroutine 调度原理与 Chanel 通信

    简介   在 Go 中,每一个并发的活动称为一个 Goroutine 或者 协程.当一个程序启动时,只有一个 Goroutine 来调用 main 函数,称之为 主Goroutine.新的 Gorou ...

  6. Go语言中Goroutine与线程的区别

    1.什么是Goroutine? Goroutine是建立在线程之上的轻量级的抽象.它允许我们以非常低的代价在同一个地址空间中并行地执行多个函数或者方法.相比于线程,它的创建和销毁的代价要小很多,并且它 ...

  7. Linux Signal及Golang中的信号处理

    转载地址:https://colobu.com/2015/10/09/Linux-Signals/ 信号(Signal)是Linux, 类Unix和其它POSIX兼容的操作系统中用来进程间通讯的一种方 ...

  8. golang 解析 --- 进程,线程,协程

    一.背景 在并发编程中进程和线程是不可忽略的两个概念,他们很好的完成了操作系统或者服务对于高并发的需求,然而随着时代的进步,协程的概念应运而生,本文旨在解释协程相对于进程和线程在高并发环境下的优势,所 ...

  9. golang的goroutine调度模型

    golang的goroutine调度模型 进程:一个在内存中运行的程序 线程:进程中的一个控制单元,一个进程至少由一个线程,也可以由多个线程,主要由CPU进行调度. 形象理解:进程就是一个生产某样产品 ...

最新文章

  1. python 数据分析学什么-python数据分析哪些课程好?
  2. 自定义控件 - 流式布局:TagFlowLayout
  3. IIS6.0官方技术必读
  4. CG-CTF-Web-php decode
  5. NSArray创建和使用
  6. GC的作用域,方法区和堆
  7. python网站后台_Python 网站后台扫描脚本
  8. jQuery 插件 autocomplete 的使用
  9. Android手机网页字体异常,移动端html5手机网站的中文字体使用
  10. 银联在线 网关支付 (JAVA版)
  11. 泰坦尼克号的数据集的下载 tensflow
  12. [ZZ]美图秀秀怎么加水印
  13. zookeeper的羊群效应
  14. P5023 填数游戏
  15. NDIS Filter Drivers指南
  16. 一个亿万富翁的创业自述
  17. apicloud加java,【APICloud】App开发中加入系统分享功能案例源码分享
  18. 两小时学会MySQL查询语句(下篇)
  19. 微信小程序学习之路——浮动与定位
  20. 全面了解大数据“三驾马车”的开源实现

热门文章

  1. JSON.parse()、eval()、JSON.stringify()、jQuery.parseJSON()的用法
  2. redis队列(list)
  3. java.lang.NoClassDefFoundError: org/apache/commons/collections/map/LRUMap 解决方法
  4. 11.1.1 认识StringBuffer类(1)
  5. [零基础学JAVA]Java SE应用部分-35.JAVA类集之二
  6. vue1和vue2获取dom元素的方法 及 nextTick() 、$nextTick()
  7. Hibernate5-一对多双向关联-迫切左外连接-HQL
  8. 第一百八十二节,jQuery-UI,知问前端--日历 UI
  9. Unity物理投射相关问题整理
  10. 常见错误:未能加载文件或程序集