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操作,在底层运行时库中对应的是一个runtime.chansend函数。
1
2
| //c <- v
void runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc)
|
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,顺序是按出现的次序
};
|
高级数据结构的实现 - channel - 《深入解析Go》 - 书栈网 · BookStack