golang 程序休眠_Golang语言并发编程之定时器

上一章中对于golang的常用关键字说明如下:接下来我们来对golang的并发编程进行说明,主要内容有:3 定时器4 Channel5 调度器6 网络轮询器7 系统监控— — — — — — — — — — — — — — — — — — — — — — — — — — — —准确的时间对于任何一个正在运行的应用非常重要,但是在一个分布式系统中我们很难保证各个节点上绝对时间的一致,哪怕通过 NTP...
摘要由CSDN通过智能技术生成

上一章中对于golang的常用关键字说明如下:

接下来我们来对golang的并发编程进行说明,主要内容有:3 定时器

4 Channel

5 调度器

6 网络轮询器

7 系统监控

— — — — — — — — — — — — — — — — — — — — — — — — — — — —

准确的时间对于任何一个正在运行的应用非常重要,但是在一个分布式系统中我们很难保证各个节点上绝对时间的一致,哪怕通过 NTP 这种标准的对时协议也只能把各个节点上时间的误差控制在毫秒级,所以准确的相对时间在分布式系统中显得更为重要,本节会分析用于获取相对时间的计时器(Timer)的设计与实现原理。

3.1 设计原理

Go 语言从实现计时器到现在经历过很多个版本的迭代,到最新的 1.14 版本为止,计时器的实现分别经历了以下几个过程:Go 1.9 版本之前,所有的计时器由全局唯一的四叉堆维护1;

Go 1.10 ~ 1.13,全局使用 64 个四叉堆维护全部的计时器,每个处理器(P)创建的计时器会由对应的四叉堆维护2;

Go 1.14 版本之后,每个处理器单独管理计时器并通过网络轮询器触发3;

我们在这一节会分别介绍计时器在不同版本的不同设计,梳理计时器实现的演进过程。

全局四叉堆

Go 1.10 之前的计时器都使用最小四叉堆实现,所有的计时器都会存储在如下所示的结构体

var timers struct {

lock mutex

gp *g

created bool

sleeping bool

rescheduling bool

sleepUntil int64

waitnote note

t []*timer

}

这个结构体中的字段 t 就是最小四叉堆,创建的所有计时器都会加入到四叉堆中。四叉堆中的计时器到期;

四叉堆中加入了触发时间更早的新计时器;

图 - 计时器四叉堆

然而全局四叉堆共用的互斥锁对计时器的影响非常大,计时器的各种操作都需要获取全局唯一的互斥锁,这会严重影响计时器的性能4。

分片四叉堆

Go 1.10 将全局的四叉堆分割成了 64 个更小的四叉堆5。在理想情况下,四叉堆的数量应该等于处理器的数量,但是这需要实现动态的分配过程,所以经过权衡最终选择初始化 64 个四叉堆,以牺牲内存占用的代价换取性能的提升。

const timersLen = 64

var timers [timersLen]struct {

timersBucket

}

type timersBucket struct {

lock mutex

gp *g

created bool

sleeping bool

rescheduling bool

sleepUntil int64

waitnote note

t []*timer

}

如果当前机器上的处理器 P 的个数超过了 64,多个处理器上的计时器就可能存储在同一个桶中。每一个计时器桶都由一个运行

图 - 分片计时器桶

将全局计时器分片的方式,虽然能够降低锁的粒度,提高计时器的性能,但是 6。

网络轮询器

在最新版本的实现中,计时器桶已经被移除7,所有的计时器都以最小四叉堆的形式存储在处理器

图 - 处理器中的最小四叉堆

处理器timersLock — 用于保护计时器的互斥锁;

timers — 存储计时器的最小四叉堆;

numTimers — 处理器中的计时器数量;

adjustTimers — 处理器中处于 timerModifiedEarlier 状态的计时器数量;

deletedTimers — 处理器中处于 timerDeleted 状态的计时器数量;

type p struct {

...

timersLock mutex

timers []*timer

numTimers uint32

adjustTimers uint32

deletedTimers uint32

...

}

原本用于管理计时器的

3.2 数据结构

type timer struct {

pp puintptr

when int64

period int64

f func(interface{}, uintptr)

arg interface{}

seq uintptr

nextwhen int64

status uint32

}when — 当前计时器被唤醒的时间;

period — 两次被唤醒的间隔;

f — 每当计时器被唤醒时都会调用的函数;

arg — 计时器被唤醒时调用 f 传入的参数;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值