一:什么是调度

平常我们在生活中会有哪些调度的例子呢?比如十字路口的红绿灯,它就是一种调度系统。在交通十字路口,每个路口上多多少少有一些车辆,为了限制这些车辆不随意行驶,就建起了红绿灯调度系统。红绿灯可以有序的调度这些车辆行驶,使这些车辆快速的通过路口。

那为什么需要红绿灯来进行调度呢?

1:使车辆有序的行驶不至于相撞

2:使车辆能快速的通过路口

上面的红绿灯系统有哪些元素呢?1. 红绿灯系统-负责调度车辆  2. 车辆  3. 十字路口

由此我们可以看出一个调度系统基本元素有2个:1. 调度系统 2. 被调度的东西 3. 应用的资源
因为资源(十字路口)是有限的,每个方向只有一条路可供车辆行驶,而车辆在每个路口又是很多的,这是不是就形成了资源和使用者之间的矛盾了,为了解决这个矛盾,红绿灯调度系统应运而生。
如果我有多层路口,比如高架桥,多了一层,那走上面的车辆是不是就不需要等红绿灯了。这应该是调度系统在生活中的启示。

所以我们可以说调度系统是协调被调度的物件合理的使用资源。

也可以回顾下以前学习Linux系统中的调度,被调度的物件是线程或者进程,资源是cpu,为了协调有限的cpu资源在多个线程(进程)中合理公平使用,就有了调度器。

那golang中的调度呢?

跟上面的红绿灯调度有一些相似点。

golang的调度是为了多个协程能合理的利用线程。这里的协程(goroutine)相当于车辆了,线程相当于十字路口(也许不恰当)。

go在运行时,会运行很多协程goroutine,也就是我们常说的并发,go为了能使这些协程有序的快速的在线程上执行,就需要进行调度了。

还有,go为了方便的控制goroutine,比如把当前线程中的goroutine移交到其他线程继续执行,从而避免应线程阻塞影响协程的运行。

还有,go中引入了GC,在执行GC的时候,要求所有goroutine停止,自己实现调度器,就可以方便实现这个功能了。

相对于操作系统的线程来说,goroutine更加的轻量,占用的内存更小,上下文进行切换时换入换出的数据也更少。

二:goroutine简介

我们说Go是语言级别的并发语言。为什么是语言级别?那不还有系统级别,对,操作系统级别就实现了多线程,多进程这种并发。

有2个支持高并发的模型:CSPActor(erlang)。Go 选择了CSP,Go为了提供更容易的并发使用方法,提供了2个重要的概念 goroutinechannel

goroutine来自协程的概念,让一组可复用的函数运行在一组线程之上,即使有协程阻塞,该线程的其他协程也可以被runtime调度,转移到其他可运行的线程上。最关键的是,程序员看不到这些底层的细节,这就降低了编程的难度,提供了更容易的并发。

goroutine非常的轻量,只占几KB,并且这几KB足够goroutine运行完。所以我们运行成千上万的goroutine成为可能,因为不会占用太多的内存资源。

channel为goroutine之间提供了通信功能。

不要通过共享内存来通信,而应该通过通信来共享内存

三:go调度模型GMP

go的调度模型,一开始并不是GPM模型,而是经过了一个发展过程。
老的调度模型只有G和M,没有P。为什么只有GM呢?因为这是一个简单的模型,最开始开发时一定容易实现。

M:代表OS线程,它是运行goroutine的

G:就是goroutine

它还有一个重要的数据结构:全局队列global runqueue。
为什么会有队列?它有什么作用?
全局队列是用来存放goroutine(G)的。启动那么多goroutine,总要一个地方把G存起来以便M来调用。

多个M会从这个全局队列里获取G来进行运行。


GM模型如下图:

M要执行G,或者把G放回去,都要访问全局队列,而且M还是多个,所以必须对全局队列加锁保证互斥。
这必然导致多个M对锁的竞争。这也是老调度器的一个缺点。

其实老调度器有4个缺点:详见Scalable Go Scheduler Design Doc

  1. 创建、销毁、调度G都需要每个M获取锁,这就形成了激烈的锁竞争
  2. M转移G会造成延迟和额外的系统开销。
  3. M中的mcache是用来存放小对象的,mcache和栈都和M关联造成了大量的内存开销和差的局部性
  4. 系统调用导致频繁的线程阻塞和取消阻塞操作增加了系统开销。

所以Go语言在2012年重新设计了调度器方案(Scalable Go Scheduler Design Doc设计方案)。

在GO1.1中新调度器引入了:

  • P(processor),它包含了运行goroutine的资源。如果线程M想运行G(goroutine),比如先获取P,P中还包含了可运行的G队列。
  • work stealing:当M绑定的P没有可运行的G时,它可以从其他运行的M那里偷取G来运行。这个的作用就是避免M因为没有可运行的G时产生饥饿的问题。

    work stealing算法地址

新调度器就是一个GPM模型了:

  • G:goroutine,用户级别的线程(协程)。我们在程序里用go关键字创建的一个协程。
  • P:processor,相当于一个处理器,它包含了goroutine运行的资源,M必须和一个P关联才能运行G。P还包含自己的本地队列(local runqueue)来保存G。为什么要搞一个本地队列?这样就可以避免竞争锁了。
  • M:工作线程,代表机器(machine)。这个线程是OS来处理的,OS负责把线程放到cpu上去运行。

当然新调度器也有一个很重要的数据结构:全局运行队列 global runqueue。

题外话:引入P,解决了老调度器的一些问题,但是同时也增加了调度器的复杂度。这个是必然的,引入新东西必然会增加系统的复杂度。就看能不能很好的解决问题,权衡利弊。

GPM模型如下图:

(图来自:https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part2.html

GRQ:全局队列 global runqueue

LRQ:P的本地队列 local runqueue

G:goroutine协程

M:OS的线程,用来绑定P,运行G,真正执行指令的人。

P:processor,goroutine运行时所需要的资源。P的数量可以用runtime.GOMAXPROCS()来控制。

每个P会分配一个LRQ(本地队列)去处理P的上下文要执行的Goroutines 。这些Goroutines会在绑定到P的M上进行上下文的切换。GRQ(全局队列)会处理还没有分配到P上的Goroutines 。

新的调度器中有全局队列,但功能已经被弱化了,当M执行work stealing从其他P偷不到G时,它可以从全局队列获取G。

抢占式调度

G-P-M模型的实现算是Go scheduler的一大进步,但Scheduler还有一个很头疼的问题,那就是不支持抢占式调度。

为什么要抢占式调度?

因为一旦某个G出现死循环或者永久循环的代码逻辑,那么G将永久占用分配给他的P和M,位于同一个P中的其他G将得不到调度,出现饿死的情况。还有一种情况,当只有一个P(GOMAXPROCS=1),

整个Go程序中的其他G都将饿死。于是Dmitry Vyukov提出了《Go Preemptive Scheduler Design》

并在Go 1.2中实现了“抢占式”调度

一些说明:

1、M绑定P,才可以不断的去运行G,如果M没有可运行的G,也可以抢占式调度(依靠sysmon)

2、从上图可以看出,每个P都有自己的本地队列,也有一个全局队列

3、M,P,G三者的数量,M默认10000,可以设置,通过SetMaxThreds修改,P默认是CPU的核数,可以设置,通过GOMAXPROCS修改,

G没有数量限制,可以创建成百上万个,甚至百万。

4、M可以与P解绑,也可以休眠

新的调度器有没有缺点?有

1、 runqueue只是一个没有优先级的队列,所以会按照先进先出的顺序来运行Goroutine。

2、调度goroutine时公平性没有很好的保证:已经提议进行修改。

3、 runqueue没有利用缓存,使用缓存[栈而不是队列]可以加速Goroutine的访问。

四:参考:

深入理解Go语言(03):scheduler调度器 - 基本介绍的更多相关文章

  1. quartz2.3.0(十二)通过RMI协议向Scheduler调度器远程添加job任务

    此代码示例通过RMI协议向Scheduler调度器远程添加job任务. 代码文件包括:job任务类(SimpleJob.java).RMI服务端server类(RemoteServerExample. ...

  2. scrapy 源码解析 (四):启动流程源码分析(四) Scheduler调度器

    Scheduler调度器 对ExecutionEngine执行引擎篇出现的Scheduler进行展开.Scheduler用于控制Request对象的存储和获取,并提供了过滤重复Request的功能. ...

  3. IO调度器原理介绍

    IO调度器(IO Scheduler)是操作系统用来决定块设备上IO操作提交顺序的方法.存在的目的有两个,一是提高IO吞吐量,二是降低IO响应时间.然而IO吞吐量和IO响应时间往往是矛盾的,为了尽量平 ...

  4. Go语言的GPM调度器是什么?

    我是平也,这有一个专注Gopher技术成长的开源项目「go home」 导读 相信很多人都听说过Go语言天然支持高并发,原因是内部有协程(goroutine)加持,可以在一个进程中启动成千上万个协程. ...

  5. Yarn 调度器Scheduler详解

    理想情况下,我们应用对Yarn资源的请求应该立刻得到满足,但现实情况资源往往是有限的,特别是在一个很繁忙的集群,一个应用资源的请求经常需要等待一段时间才能的到相应的资源.在Yarn中,负责给应用分配资 ...

  6. Yarn 组件的指挥部 – 调度器Scheduler

    linux基础 为hadoop集群的搭建扫清了障碍,也为内存的管理,文件系统的管理扫清了障碍 接着到Hadoop的阶段,首先做集群的安装,深入到使用这两个核心的组件,分布式文件系统HDFS,解决大量数 ...

  7. hadoop之 Yarn 调度器Scheduler详解

    概述 集群资源是非常有限的,在多用户.多任务环境下,需要有一个协调者,来保证在有限资源或业务约束下有序调度任务,YARN资源调度器就是这个协调者. YARN调度器有多种实现,自带的调度器为Capaci ...

  8. 调度器简介,以及Linux的调度策略

    进程是操作系统虚拟出来的概念,用来组织计算机中的任务.但随着进程被赋予越来越多的任务,进程好像有了真实的生命,它从诞生就随着CPU时间执行,直到最终消失.不过,进程的生命都得到了操作系统内核的关照.就 ...

  9. 调度器简介,以及Linux的调度策略(转)

    进程是操作系统虚拟出来的概念,用来组织计算机中的任务.但随着进程被赋予越来越多的任务,进程好像有了真实的生命,它从诞生就随着CPU时间执行,直到最终消失.不过,进程的生命都得到了操作系统内核的关照.就 ...

  10. Linux内核——进程管理之CFS调度器(基于版本4.x)

    <奔跑吧linux内核>3.2笔记,不足之处还望大家批评指正 建议阅读博文https://www.cnblogs.com/openix/p/3262217.html理解linux cfs调 ...

随机推荐

  1. [转帖]【基础】HTTP、TCP/IP 协议的原理及应用

    https://juejin.cn/post/6844903938232156167 前言 本文将持续记录笔者在学习过程中掌握的一些 HTTP .TCP/IP 的原理,以及这些网络通信技术的一些应用场 ...

  2. [转帖]k8s之PV、PVC、StorageClass详解

    https://zhuanlan.zhihu.com/p/128552232 导读 上一篇写了共享存储的概述以及一个简单的案例演示.这一篇就写一下PV和PVC. PV是对底层网络共享存储的抽象,将共享 ...

  3. Notepad++ 显示空格

    公司里面用yaml 文件经常会出现一些奇怪的问题... 今天又遇到了//全角空格 显示的长度一样 但是实际上 yaml文件解析的不对..notepad++ ---> 视图--->显示符号- ...

  4. 从源代码构建TensorFlow流程记录

    京东科技隐私计算产品部 曹雨晨 为什么从源代码构建 通常情况下,直接安装构建好的.whl即可.不过,当需要一些特殊配置(或者闲来无事想体会 TensorFlow 构建过程到底有多麻烦)的时候,则需要选 ...

  5. vue获取子组件的实例$el、$attrs和inheritAttrs的使用

    我的需求 有些时候,我们需要获取组件的DOM元素 有些小伙伴会说,这还不简单 直接使用this.$ref.xx不就可以了吗 我们来看一下,是不是我们想的那样简单 组件内容 <template&g ...

  6. void的讲解 、any的讲解 、联合类型的讲解

    1. void的使用 空值一般采用 void 来表示,同时void也可以表示变量 也可以表示函数没有返回值哈 使用了 void 就不能够使用 return 哈 let sum = function() ...

  7. Gorm日志设置

    Logger Gorm提供了一个默认的logger实现,默认情况下日志数据级别为warn,同时输出慢SQL: Default = New(log.New(os.Stdout, "\r\n&q ...

  8. vim 从嫌弃到依赖(8)——使用命令模式编辑文本

    通过前面的文章,我们已经介绍了vim的普通模式.插入模式.可视模式.接下来让我们接着介绍vim中另一个强大的模式--命令行模式 命令模式简介 命令模式可以说在vim中的使用频率不亚于普通模式,像我们平 ...

  9. 深度学习基础入门篇[六(1)]:模型调优:注意力机制[多头注意力、自注意力],正则化【L1、L2,Dropout,Drop Connect】等

    深度学习基础入门篇[六(1)]:模型调优:注意力机制[多头注意力.自注意力],正则化[L1.L2,Dropout,Drop Connect]等 1.注意力机制 在深度学习领域,模型往往需要接收和处理大 ...

  10. 8.6 C++ 泛型化编程态

    C/C++语言是一种通用的编程语言,具有高效.灵活和可移植等特点.C语言主要用于系统编程,如操作系统.编译器.数据库等:C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统.图形用户界面 ...