go channel源码分析

2018-12-16  本文已影响40人  Jancd

go 源码文件:/runtime/chan.go

type hchan struct {
    qcount   uint           // total data in the queue(队列中数据个数)
    dataqsiz uint           // size of the circular queue(缓冲槽大小)
    buf      unsafe.Pointer // points to an array of dataqsiz elements (指向缓冲槽的指针)
    elemsize uint16
    closed   uint32
    elemtype *_type // element type(数据类型)
    sendx    uint   // send index(发送位置索引)
    recvx    uint   // receive index(接收位置索引)
    recvq    waitq  // list of recv waiters (接收等待列表)
    sendq    waitq  // list of send waiters (发送等待列表)

    // lock protects all fields in hchan, as well as several
    // fields in sudogs blocked on this channel.
    //
    // Do not change another G's status while holding this lock
    // (in particular, do not ready a G), as this can deadlock
    // with stack shrinking.
    lock mutex
}

你可以简单粗暴的把channel的实现理解为 队列 + 锁

值得注意的是 sendqrecvq字段,它们的类型是 waitq:

type waitq struct {
    first *sudog
    last  *sudog
}

这个 waitq 也就是阻塞在channelG(注:这里的G代表着 G-M-P模型中的G)列表。在 csp 模型中 G 可以同时阻塞在不同的 channel 上,于是对 G 进行了封装,引入了 sudog:

type sudog struct {
    // The following fields are protected by the hchan.lock of the
    // channel this sudog is blocking on. shrinkstack depends on
    // this for sudogs involved in channel ops.

    g *g

    // isSelect indicates g is participating in a select, so
    // g.selectDone must be CAS'd to win the wake-up race.
    isSelect bool
    next     *sudog
    prev     *sudog
    elem     unsafe.Pointer // data element (may point to stack)

    // The following fields are never accessed concurrently.
    // For channels, waitlink is only accessed by g.
    // For semaphores, all fields (including the ones above)
    // are only accessed when holding a semaRoot lock.

    acquiretime int64
    releasetime int64
    ticket      uint32
    parent      *sudog // semaRoot binary tree
    waitlink    *sudog // g.waiting list or semaRoot
    waittail    *sudog // semaRoot
    c           *hchan // channel
}

channel 的创建


func makechan(t *chantype, size int) *hchan {
    elem := t.elem

    // compiler checks this but be safe.
    if elem.size >= 1<<16 {    //不大于64k
        throw("makechan: invalid channel element type")
    }
    if hchanSize%maxAlign != 0 || elem.align > maxAlign {
        throw("makechan: bad alignment")
    }

    if size < 0 || uintptr(size) > maxSliceCap(elem.size) || uintptr(size)*elem.size > maxAlloc-hchanSize {
        panic(plainError("makechan: size out of range"))
    }

    // Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers.
    // buf points into the same allocation, elemtype is persistent.
    // SudoG's are referenced from their owning thread so they can't be collected.
    // TODO(dvyukov,rlh): Rethink when collector can move allocated objects.
    var c *hchan
    switch {  //根据实际情况分配内存。
    case size == 0 || elem.size == 0:
        // Queue or element size is zero.
        c = (*hchan)(mallocgc(hchanSize, nil, true))
        // Race detector uses this location for synchronization.
        c.buf = c.raceaddr()
    case elem.kind&kindNoPointers != 0:
        // Elements do not contain pointers.
        // Allocate hchan and buf in one call.
        c = (*hchan)(mallocgc(hchanSize+uintptr(size)*elem.size, nil, true))
        c.buf = add(unsafe.Pointer(c), hchanSize)
    default:
        // Elements contain pointers.
        c = new(hchan)
        c.buf = mallocgc(uintptr(size)*elem.size, elem, true)
    }

    c.elemsize = uint16(elem.size)
    c.elemtype = elem
    c.dataqsiz = uint(size)

    if debugChan {
        print("makechan: chan=", c, "; elemsize=", elem.size, "; elemalg=", elem.alg, "; dataqsiz=", size, "\n")
    }
    return c
}

未完待续。。

上一篇下一篇

猜你喜欢

热点阅读