博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
goroutine 相关知识9
阅读量:5821 次
发布时间:2019-06-18

本文共 2291 字,大约阅读时间需要 7 分钟。

hot3.png

go runtime M:N模型可以根据具体的操作类型(操作系统阻塞或非阻塞操作)调整goroutine和OS Thread的映射情况,更加灵活

两个M,即两个OS Thread线程,分别对应一个P,每一个P有负责调度多个G。组成的goroutine运行时的基本结构

G最重要的三个状态

  • Grunnable

  • Grunning

  • Gwaiting

状态迁移 Grunnable -> Grunning -> Gwaiting -> Grunnable

goroutine在状态发生转变时,会对栈的上下文进行保存和恢复

G中的Gobuf的定义

struct Gobuf{ uintptr sp; // 栈指针 uintptr pc; // 程序计数器PC G* g; // 关联的G};

保存栈上下文时,最重要的就是保存这个Gobuf结构中的内容

具体是通过 void gosave(Gobuf*) 以及 void gogo(Gobuf*) 实现栈上下文的保存和恢复

底层实现为汇编,因此goroutine的context swtich非常快


goroutine scheduler在几个主要场景下的调度策略

goroutine将scheduler的执行交给具体的M,即OS Thread

每一个M就执行一个函数,即 void schedule(void)

这个函数具体做得事情就是从各个运行队列中选择合适的goroutine然后执行goroutine中对应的 func

// 调度的一个回合:找到可以运行的G,执行// 从不返回static void schedule(void){ G *gp; uint32 tick;top: gp = nil;// 时不时检查全局的可运行队列,确保公平性// 否则两个goroutine不断地互相重生,完全占用本地的可运行队列 tick = m->p->schedtick;// 优化技巧,其实就是tick%61 == 0if(tick - (((uint64)tick\*0x4325c53fu)>>36)\*61==0&& runtime·sched.runqsize >0) { runtime·lock(&runtime·sched); gp = globrunqget(m->p, 1);// 从全局可运行队列获得可用的G runtime·unlock(&runtime·sched);if(gp) resetspinning(); }if(gp == nil) { gp = runqget(m->p); // 如果全局队列里没找到,就在P的本地可运行队列里找if(gp && m->spinning) runtime·throw("schedule: spinning with local work"); }if(gp == nil) { gp = findrunnable(); // 阻塞住,直到找到可用的G resetspinning(); }// 是否启用指定某M来执行该Gif(gp->lockedm) {// 把P给指定的m,然后阻塞等新的P startlockedm(gp); goto top; } execute(gp); // 执行G}

几个问题:

  1. 当M发现分配给自己的goroutine链表已经执行完毕时怎么办?
  2. 当goroutine陷入系统调用阻塞后,M是否也一起阻塞?
  3. 当某个gorouine长时间占用CPU怎么办?

  • 从其他的P中偷取goroutine然后执行,略就是工作密取的机制

  • 当其他的P也没有可执行的goroutine时,从全局等待队列中寻找runnable的goroutine进行执行

  • 如果还找不到,则M让出CPU调度

例如阻塞IO读取本地文件,此时调用会systemcall会陷入内核,不可避免地会使得调用线程阻塞

goroutine将所有可能阻塞的系统调用均封装为gorouine友好的接口

具体,在每次进行系统调用之前,从一个线程池从获取一个OS Thread并执行该系统调用,而本来运行的gorouine则将自己的状态改为Gwaiting,并将控制权交给scheduler继续调度,系统调用的返回通过channel进行同步即可

因此其实goroutine也没有办法做到完全的协程化,因为系统调用总会阻塞线程

go支持简单的抢占式调度,sysmon线程检测goruntime的各种状态,长时间占用CPU的goroutine,如果发现了就将其抢占过来


goroutine 中最重要的一个设计就在于它将所有的语言层次上的api都限制在了goroutine这一层,进而屏蔽了执行代码与具体线程交互的机会

实际上就可以忽略线程的存在,把goroutine当成是一个非常廉价能够大量创建的Thread


JVM系语言(如scala,clojure),本质上都无法完全实现goroutine(clojure虽然有async,但是依然无法和JDK中的阻塞api结合良好)

假设在Java中基于Thread之上实现一个scheduler,一个轻量级的协程以及协程相关的原语(如resume, pause等),我们也只能基于我们自己封装的api来协助协程调度

如果在创建的协程中直接使用Java阻塞api,结果就是使用来调度协程的OS Thread陷入阻塞,无法继续运行scheduler进行调度

转载于:https://my.oschina.net/zhangthe9/blog/3022938

你可能感兴趣的文章
Java算法练习——罗马数字转整数
查看>>
vue:自定义指令
查看>>
java_24.1文件流的应用--复制文件
查看>>
Oracle中的dual
查看>>
Python3安装
查看>>
Openstack的用户登录流程
查看>>
第一章 Web MVC简介
查看>>
as3 加载库声音报错
查看>>
自我介绍
查看>>
无人便利店系统代理发展前景分析
查看>>
git的介绍及使用
查看>>
python-Selenium库的详解
查看>>
百度究竟有没有必要购买PPS?
查看>>
redisi配置安装
查看>>
基础知识(5)- 继承
查看>>
CSharp设计模式读书笔记(23):模板方法模式(学习难度:★★☆☆☆,使用频率:★★★☆☆)...
查看>>
用include()和ob_get_contents( )方法 生成静态文件
查看>>
ekho安装及测试(中文文字转语音)
查看>>
php判断 二维数组中 是否 存在某个一维数组
查看>>
day12 Python字典
查看>>