1、什么是Goroutine?

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

package main

import (
"fmt"
"time"
) func learning() {
fmt.Println("My first goroutine")
} func main() {
go learning()
time.Sleep(1 * time.Second)
fmt.Println("main function")
}

这段代码的输出是:

My first goroutine

main 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)。因此goroutine的切换效率更高。

3、Goroutine的调度

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

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

调度器具体是如何工作的呢,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并执行直到其被阻塞。由此可见,操作系统的一个线程下可以并发执行上千个goroutine,每个goroutine所占用的资源和切换开销都很小,因此,goroutine是golang适合高并发场景的重要原因。

Go语言中Goroutine与线程的区别的更多相关文章

  1. 浅谈Java语言中ArrayList和HashSet的区别

    Java语言中ArrayList和HashSet的区别 2019-04-10   13:22:49 一.基本区别 首先一起看个实例,其代码如下: package com.MrZ_baby.com; i ...

  2. goroutine 和 线程的区别

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

  3. 面试题----C语言中exit和return的区别

    C语言中return和exit的区别 exit用于结束进程,返回的状态码是给操作系统使用或父进程使用的.return是堆栈返回,返回的值是给主调函数用的.主线程结束前会默认调用exit结束进程. ex ...

  4. goroutine 和线程的区别

    好久没写点儿啥了,强行更新一下. 1,从使用上讲 1,goroutine 比线程更轻量级,可以创建十万.百万不用担心资源问题. 2,goroutine 和 chan 搭配使用,实现多线程.高并发 实现 ...

  5. Go语言中的byte和rune区别、对比

    Go语言中byte和rune实质上就是uint8和int32类型.byte用来强调数据是raw data,而不是数字:而rune用来表示Unicode的code point.参考规范: uint8 t ...

  6. Go语言中new和make的区别

    Go语言中new跟make是内置函数,主要用来创建分配类型内存. new( ) new(T)创建一个没有任何数据的类型为T的实例,并返回该实例的指针: 源码解析 func new func new(T ...

  7. Java编程语言中sleep()和yield()的区别

    转自:http://developer.51cto.com/art/201003/189465.htm 1. Thread.yield():     api中解释: 暂停当前正在执行的线程对象,并执行 ...

  8. C语言中exit()与return的区别

    整理自exit函数和return函数 1.exit函数和return函数的主要区别是: 1)exit用于在程序运行的过程中随时结束程序,exit的参数是返回给OS的.main函数结束时也会隐式地调用e ...

  9. Go语言中Goroutine的设置

    一. 通过runtime包进行多核设置 1.NumCPU()获取当前系统的cpu核数 2.GOMAXPROCS设置当前程序运行时占用的cpu核数 版本1.6之前默认是使用1个核,而之后是全部使用. 好 ...

随机推荐

  1. Python 3 列表

    列表:是可变的序列,也是一种可以存储各种数据类型的集合,用中括号([])表示列表的开始和结束,元素之间用逗号(,)分隔.列表中每个元素提供一个对应的下标. 1.列表的基本格式表示: 2.列表的不同数据 ...

  2. [深入理解JVM虚拟机]第3章-垃圾收集器、内存分配策略

    垃圾收集器 判断对象是否需存活 回收堆 判断对象是否存活: 方法一:引用计数法.对象被引用一次就+1,当为0时回收对象.缺点:无法解决循环引用问题. 方法二:可达性分析算法.记录当前对象是否有和GC ...

  3. JavaGuide易错点总结

    基础知识易错点 1. object.equals("str") 容易报空指针异常,应使用"str".equals(object); 还可以使用JDK7引入的工具 ...

  4. switch-case 选择语句

    0. 语句模型 Go 里的选择语句模型是这样的 switch 表达式 { case 表达式1: 代码块 case 表达式2: 代码块 case 表达式3: 代码块 case 表达式4: 代码块 cas ...

  5. Redis 发布订阅,小功能大用处,真没那么废材!

    今天小黑哥来跟大家介绍一下 Redis 发布/订阅功能. 也许有的小伙伴对这个功能比较陌生,不太清楚这个功能是干什么的,没关系小黑哥先来举个例子. 假设我们有这么一个业务场景,在网站下单支付以后,需要 ...

  6. dubbo学习(一)认识dubbo

    一.发展背景 单一应用架构 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本.此时,用于简化增删改查工作量的数据访问框架(ORM)是关键. 垂直应用架构 当访问量逐渐增大, ...

  7. Kubernetes K8S之存储Secret详解

    K8S之存储Secret概述与类型说明,并详解常用Secret示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 ...

  8. Git-使用Rebase合并分支

    commit 合并 在开发过程中,可能会出现多个 commit 所涉及的逻辑都是同一个功能模块,此时,会导致 log tree 非常的混乱,不美观,因此,我们可以将多个 commit 进行合并,变成一 ...

  9. Spring学习(一)--Spring的设计与整体架构

    之前只是在学校里大概的学习了一下Spring框架的使用以及一些最基本.浅显的原理,并没有做出深入的学习,等到工作之后想提升自己的时候发现所掌握的Spring框架的简直烂如狗屎,为监督自己的学习进度,立 ...

  10. Python-面向网络编程-socket原理

    socket 整个计算机网络是由协议构成,想要通信必须遵守对应的协议,如Web中的http协议.传输协议TCP和UDP等等.在网络工程师的眼中,可能现在网络上的一切都是socket,一切皆socket ...