Go Channel

简介

  • channel是first-class的,可以被存储到变量中,可以作为参数传递给函数,也可以作为函数的返回值返回;

  • channel分为:有缓存channel和无缓冲channel两种;

  • close:如果 Channel 是一个空指针或者已经被关闭时,Go 语言运行时都会直接崩溃并抛出异常;

  • 从一个nil channel中接收数据会一直被block;

  • 从一个被closed的channel中接收数据会立即返回;

定义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
struct    Hchan
{
    uintgo    qcount;            // 队列q中的总数据数量
    uintgo    dataqsiz;        // 环形队列q的数据大小
    uint16    elemsize;        //队列的当前使用量

    bool    closed;
    uint8    elemalign;
    Alg*    elemalg;        // interface for element type
    uintgo    sendx;            // 发送index
    uintgo    recvx;            // 接收index
    WaitQ    recvq;            // 因recv而阻塞的等待队列
    WaitQ    sendq;            // 因send而阻塞的等待队列
    Lock;
};

带缓冲区的chan,则缓冲区数据实际上是紧接着Hchan结构体中分配的

1
c = (Hchan*)runtime.mal(n + hint*elem->size);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
struct    WaitQ
{
    SudoG*    first;
    SudoG*    last;
};

//
struct    SudoG
{
    G*    g;        // g and selgen constitute
    uint32    selgen;        // a weak pointer to g
    SudoG*    link;
    int64    releasetime;
    byte*    elem;        // data element
};

读写channel操作

写channel操作,在底层运行时库中对应的是一个runtime.chansend函数。

1
2
//c <- v
void runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc)

select的实现

select-case中的chan操作编译成了if-else。比如:

每个select都对应一个Select结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
select {
case v = <-c:
        ...foo
default:
        ...bar
}


// 编译如下:
if selectnbrecv(&v, c) {
        ...foo
} else {
        ...bar
}

select和case关键字使用了下面的结构体:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
struct    Scase
{
    SudoG    sg;            // must be first member (cast to Scase)
    Hchan*    chan;        // chan
    byte*    pc;            // return pc
    uint16    kind;
    uint16    so;            // vararg of selected bool
    bool*    receivedp;    // pointer to received bool (recv2)
};
struct    Select
{
    uint16    tcase;            // 总的scase[]数量
    uint16    ncase;            // 当前填充了的scase[]数量
    uint16*    pollorder;        // case的poll次序
    Hchan**    lockorder;        // channel的锁住的次序
    Scase    scase[1];        // 每个case会在结构体里有一个Scase,顺序是按出现的次序
};

参考

  1. 高级数据结构的实现 - channel - 《深入解析Go》 - 书栈网 · BookStack