zoukankan      html  css  js  c++  java
  • golang的select实现原理剖析

    写在最前面

    select为golang提供了多路IO复用机制,和其他IO复用一样,用于检测是否有读写事件是否ready。

    本文将介绍一下golang的select的用法和实现原理。

    实现原理

    golang实现select的时候,实际上为每一个case语句定义了一个数据结构,select语句块执行的时候,实际上可以类比成对一个case数组处理的代码块(或者函数),然后程序流程转到选中的case块。

    case数据结构

    源码包src/runtime/select.go:scase定义了表示case语句的数据结构:

    type scase struct {
    	c           *hchan         // chan
    	kind        uint16
    	elem        unsafe.Pointer // data element
    }
    

      scase.c表示当前case语句操作的chan指针,这也表明一个case只能监听一个chan。

      scase.kind表示当前的chan是可读还是可写channel或者是default。三种类型分别由常量定义:

    • caseRecv:case语句中尝试读取scase.c中的数据;
    • caseSend:case语句中尝试向scase.c中写入数据;
    • caseDefault: default语句

      scase.elem表示缓冲区地址,跟据scase.kind不同,有不同的用途:

    • scase.kind == caseRecv : scase.elem表示读出channel的数据存放地址;
    • scase.kind == caseSend : scase.elem表示将要写入channel的数据存放地址;

    select实现逻辑

    源码包src/runtime/select.go:selectgo()定义了select选择case的函数:

    // selectgo implements the select statement.
    //
    // *sel is on the current goroutine's stack (regardless of any
    // escaping in selectgo).
    //
    // selectgo returns the index of the chosen scase, which matches the
    // ordinal position of its respective select{recv,send,default} call.
    func selectgo(sel *hselect) int {
    }

      其中数据结构hselect如下:

    // Select statement header.
    // Known to compiler.
    // Changes here must also be made in src/cmd/internal/gc/select.go's selecttype.
    type hselect struct {
    	tcase     uint16   // total count of scase[]
    	ncase     uint16   // currently filled scase[]
    	pollorder *uint16  // case poll order
    	lockorder *uint16  // channel lock order
    	scase     [1]scase // one per case (in order of appearance)
    }
    

    hselect.tcase存的是scase总数。

    hselect.pollorder是保存scase的随机后的序列。以达到随机检测case的目的。

    hselect.lockorder是保存的channel地址。所有case语句中channel序列,以达到去重防止对channel加锁时重复加锁的目的。

    selectgo返回int,表示选中的scase,也就是ready的channel index。

    该函数执行逻辑大致如下:

    1. 锁定scase语句中所有的channel

    2. 按照随机顺序检测scase中的channel是否ready

      2.1 如果case可读,则读取channel中数据,解锁所有的channel,然后返回(case index)

      2.2 如果case可写,则将数据写入channel,解锁所有的channel,然后返回(case index)

      2.3 所有case都未ready,则解锁所有的channel,然后返回(default index)

    3. 所有case都未ready,且没有default语句

       3.1 将当前协程加入到所有channel的等待队列

       3.2 当将协程转入阻塞,等待被唤醒

    4. 唤醒后返回channel对应的case index

      4.1 如果是读操作,解锁所有的channel,然后返回(case index)

      4.2 如果是写操作,解锁所有的channel,然后返回(case index)

  • 相关阅读:
    win7 64位系统,vs2010下配置OpenGL开发环境
    OpenCV stereo matching 代码 matlab实现视差显示
    Cocos2d-x 3.x游戏开发之旅
    芯片验证漫游指南
    名师讲坛:PHP开发实战权威指南
    Python带我起飞:入门、进阶、商业实战
    新编Excel会计与财务管理应用大全(2016实战精华版)
    CorelDRAW X7中文版完全自学宝典
    HTML5 canvas开发详解(第2版)
    中文版3ds Max 2014--VRay效果图制作实用教程
  • 原文地址:https://www.cnblogs.com/howo/p/10507934.html
Copyright © 2011-2022 走看看