热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

golang调度机制

点击上方“黑光技术”,选择“设


点击上方“黑光技术”,选择“设为星标”

完美之道,不在无可增加,而在无可删减!

黑光技术专栏

 
  • Golang UnitTest单元测试

  • Golang官方依赖管理工具dep学习使用

  • Golang信号处理和如何实现进程优雅退出

  • golang的httpserver优雅重启

  • golua虚拟机的使用

Golang的核心之一gorountine

go语言非常重要的一个特性就是gorountine,有了这个东东,就可以很简单的做并发处理程序,比起c 和java的方式来说可以说简单了很多很多。那么gorountine又是一个什么样的东东呢? 从使用上来看它就是一个函数,使用起来有点像thread,但是实际上又不是,thread我们一般说起是指内核中的调度单元,他也是又用户态传递一个函数给thread,再由内核来调度执行,而gorountine是完全用户态的一个东西,而它要想和thread一样被执行那么就需要设计一个用户态的调度器,来保存它结构,执行现场和调度切换不同的gorountine进行执行。更多的说是一种叫做协程的东西,完全由用户态程序控制。这和C、C 完全不一样,C、C 的语言编译之后执行完全是交给操作系统内核来控制执行,而golang,在编译时会加入自己的调度器代码,在执行上按照自己的调度器进行调度执行。


调度器必要的作用有那些


调度数据结构

调度器的数据结构的设计是调度器中的核心要素,队列怎么组织,最简单的队列组织方式就是像链表,双向链表,复杂点的由结构体在组织,结构体中增加queen,list,hashmap,set等等。不同的组织方式会直接影响调度器的调度算法和性能。


调度算法

对应于数据结构的组织,就是调度算法,很多时候数据结构的设计就是为了算法的实现更方便,简单的算法如fifo,另外复杂的根据权重调度,时间片调度,根据优先级调度,任务大小调度等等。还要包括任务抢占等。


调度时环境管理

这里说的调度时环境管理在gorountine调度中主要就是指的它的堆栈管理,我们指导gorountine是一个用户态级别的运行,所以它的堆栈也必然是在用户态的调度器中进行管理的。在golang中每一个gorountine产生事都会给分配一片用户态内存用做自己的堆栈,在Golang的栈管理中还使用了连续栈实现方式也先分配一块固定大小的栈,在栈空间不足时,分配一块更大的栈,并把旧的栈全部拷贝到新栈中。


gorountine调度器的设计


gorountine的调度器的主要数据结构:

G 代表一个Goroutine;存储了goroutine的执行stack信息、goroutine状态以及goroutine的任务函数等;另外G对象是可以重用的。

M 代表一个操作系统的线程;M代表着真正的执行计算资源。在绑定有效的p后,进入schedule循环;而schedule循环的机制大致是从各种队列、p的本地队列中获取G,切换到G的执行栈上并执行G的函数,调用goexit做清理工作并回到m,如此反复。M并不保留G状态,这是G可以跨M调度的基础。

P 代表一个逻辑CPU处理器(在golang中这个是一个对cpu的抽象),通过runtime.GOMAXPROCS (numLogicalProcessors)可以控制多少P,但是通常P的数量设置是等于CPU核数(GOMAXPROCS)。P的数量决定了系统内最大可并行的G的数量(前提:系统的物理cpu核数>=P的数量);P的最大作用还是其拥有的各种G对象队列、链表、一些cache和状态。

三者都在runtime2.go中定义,https://go.googlesource.com/go/ /go1.10.1/src/runtime/runtime2.go

type g struct {
...
stack stack // offset known to runtime/cgo...
m *m // current m; offset known to arm liblink...
}

这个结构体的字段用来跟踪此goroutine的栈(stack)和状态,所以你可以认为G = goroutine。

type m struct {
g0 *g // goroutine with scheduling stack...
curg *g // current running goroutinecaughtsig guintptr // goroutine running during fatal signalp puintptr // attached p for executing go code (nil if not executing go code)nextp puintptr
...
}

运行时管理着G并把它们映射到逻辑处理器(称之为P). P可以看作是一个抽象的资源或者一个上下文,它需要获取以便操作系统线程(称之为M)可以运行G。

type p struct {
...
m muintptr // back-link to associated m (nil if idle)...
}


GPM之间的关系如下:


  1. G需要绑定在M上才能运行;

  2. M需要绑定P才能运行;

  3. 程序中的多个M并不会同时都处于执行状态,最多只有GO MAXPROCS个M在执行。

  4. 每个P维护一个G队列;

  5. 当一个G被创建出来,或者变为可执行状态时,就把他放到P的 可执行队列中;

  6. 当一个G执行结束时,P会从队列中把该G取出;如果此时P的队列为空,即没有其他G可以执行, 就随机选择另外一个P,从其可执行的G队列中偷取一半(work-stealing调度算法)。

为了运行goroutine, M需要持有上下文P。M会从P的queue弹出一个goroutine并执行。当你创建一个新的goroutine的时候(go func()方法),它会被放入P的queue。

当你的goroutine执行阻塞的系统调用的时候(syscall),阻塞的系统调用会中断(intercepted),如果当前有一些G在执行,运行时会把这个线程从P中摘除(detach),然后再创建一个新的操作系统的线程(如果没有空闲的线程可用的话)来服务于这个P。

当系统调用继续的时候,这个goroutine被放入到本地运行queue,线程会park它自己(休眠), 加入到空闲线程中。 Go运行时会在下面的goroutine被阻塞的情况下运行另外一个goroutine:

  • blocking syscall (for example opening a file),

  • network input,

  • channel operations,

  • primitives in the sync package.

struct Sched {
Lock; // global sched lock .// must be held to edit G or M queuesG ∗gfree; // available g’ s ( status == Gdead)G ∗ghead; // g’ s waiting to run queueG ∗gtail; // tail of g’ s waiting to run queueint32 gwait; // number of g’s waiting to runint32 gcount; // number of g’s that are aliveint32 grunning; // number of g’s running on cpu// or in syscallM ∗mhead; // m’s waiting for workint32 mwait; // number of m’s waiting for workint32 mcount; // number of m’s that have been created…
};


总结

在内核不支持协程的情况之下,在用户态做更细粒度的任务调度已经是大势所趋,而且目前来效果也非常不错,golang中的这种调度设计还是非常巧妙的。

相关阅读:

  • Golang UnitTest单元测试

  • Golang官方依赖管理工具dep学习使用

  • Golang信号处理和如何实现进程优雅退出

  • golang的httpserver优雅重启

  • golua虚拟机的使用

看完本文有收获?请分享给更多人

关注「黑光技术」加星标,关注大数据 微服务

看完,赶紧点个“好看”呀

点这里

↓↓↓↓



推荐阅读
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+Loope ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • 本文介绍如何使用OpenCV和线性支持向量机(SVM)模型来开发一个简单的人脸识别系统,特别关注在只有一个用户数据集时的处理方法。 ... [详细]
  • 探讨如何在Go语言中高效地处理大规模切片的去重操作,特别是针对百万级数据量的场景。 ... [详细]
  • 题目解析给定 n 个人和 n 种书籍,每个人都有一个包含自己喜好的书籍列表。目标是计算出满足以下条件的分配方案数量:1. 每个人都必须获得他们喜欢的书籍;2. 每本书只能分配给一个人。通过使用深度优先搜索算法,可以系统地探索所有可能的分配组合,确保每个分配方案都符合上述条件。该方法能够有效地处理这类组合优化问题,找到所有可行的解。 ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 在Linux系统中,网络配置是至关重要的任务之一。本文详细解析了Firewalld和Netfilter机制,并探讨了iptables的应用。通过使用`ip addr show`命令来查看网卡IP地址(需要安装`iproute`包),当网卡未分配IP地址或处于关闭状态时,可以通过`ip link set`命令进行配置和激活。此外,文章还介绍了如何利用Firewalld和iptables实现网络流量控制和安全策略管理,为系统管理员提供了实用的操作指南。 ... [详细]
  • C语言是计算机科学和编程领域的基石,许多初学者在学习过程中会感到困惑。本文将详细介绍C语言的基本概念、关键语法和实用示例,帮助你快速上手C语言。 ... [详细]
  • 第14周实践项目(4)-验证平衡二叉树
    问题**Copyright(c)2015,烟台大学计算机学院*Allrightsreserved.*文件名称:test.cpp*作者:王敏*完成日 ... [详细]
  • 本文将深入探讨 iOS 中的 Grand Central Dispatch (GCD),并介绍如何利用 GCD 进行高效多线程编程。如果你对线程的基本概念还不熟悉,建议先阅读相关基础资料。 ... [详细]
  • 本文详细介绍了如何使用Python的多进程技术来高效地分块读取超大文件,并将其输出为多个文件。通过这种方式,可以显著提高读取速度和处理效率。 ... [详细]
  • MySQL初级篇——字符串、日期时间、流程控制函数的相关应用
    文章目录:1.字符串函数2.日期时间函数2.1获取日期时间2.2日期与时间戳的转换2.3获取年月日、时分秒、星期数、天数等函数2.4时间和秒钟的转换2. ... [详细]
  • poj 3352 Road Construction ... [详细]
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
author-avatar
手机用户2602903963
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有