go語言的chan

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

chan是一個FIFO隊列,chan分成兩種類型同步和非同步
同步的chan完成寄件者和接受者之間手遞手傳遞元素的過程,必須要求對方的存在才能完成一次發送或接受
非同步chan發送和接受都是基於chan的緩衝,但當緩衝隊列填滿後,寄件者就會進入發送隊列, 當緩衝隊列為空白時,接受者就會接入等待隊列。

chan的資料結構:

structHchan{    uintgoqcount;// total data in the q    uintgodataqsiz;// size of the circular q    uint16elemsize;    uint16pad;// ensures proper alignment of the buffer that follows Hchan in memory    boolclosed;    Alg*elemalg;// interface for element type    uintgosendx;// send index    uintgorecvx;// receive index    WaitQrecvq;// list of recv waiters    WaitQsendq;// list of send waiters    Lock;};

chan發送

voidruntime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc){    SudoG *sg;    SudoG mysg;    G* gp;    int64 t0;     if(c == nil) {    USED(t);    if(pres != nil) {    *pres = false;    return;    }    runtime·park(nil, nil, "chan send (nil chan)");    return;  // not reached    }     if(debug) {    runtime·printf("chansend: chan=%p; elem=", c);    c->elemalg->print(c->elemsize, ep);    runtime·prints("\n");    }     t0 = 0;    mysg.releasetime = 0;    if(runtime·blockprofilerate > 0) {    t0 = runtime·cputicks();    mysg.releasetime = -1;    }     runtime·lock(c);    if(raceenabled)    runtime·racereadpc(c, pc, runtime·chansend);    if(c->closed)    goto closed;     if(c->dataqsiz > 0)    goto asynch;     sg = dequeue(&c->recvq);    if(sg != nil) {    if(raceenabled)    racesync(c, sg);    runtime·unlock(c);     gp = sg->g;    gp->param = sg;    if(sg->elem != nil)    c->elemalg->copy(c->elemsize, sg->elem, ep);    if(sg->releasetime)    sg->releasetime = runtime·cputicks();    runtime·ready(gp);     if(pres != nil)    *pres = true;    return;    }     if(pres != nil) {    runtime·unlock(c);    *pres = false;    return;    }     mysg.elem = ep;    mysg.g = g;    mysg.selgen = NOSELGEN;    g->param = nil;    enqueue(&c->sendq, &mysg);    runtime·park(runtime·unlock, c, "chan send");     if(g->param == nil) {    runtime·lock(c);    if(!c->closed)    runtime·throw("chansend: spurious wakeup");    goto closed;    }     if(mysg.releasetime > 0)    runtime·blockevent(mysg.releasetime - t0, 2);     return; asynch:    if(c->closed)    goto closed;     if(c->qcount >= c->dataqsiz) {    if(pres != nil) {    runtime·unlock(c);    *pres = false;    return;    }    mysg.g = g;    mysg.elem = nil;    mysg.selgen = NOSELGEN;    enqueue(&c->sendq, &mysg);    runtime·park(runtime·unlock, c, "chan send");     runtime·lock(c);    goto asynch;    }     if(raceenabled)    runtime·racerelease(chanbuf(c, c->sendx));     c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), ep);    if(++c->sendx == c->dataqsiz)    c->sendx = 0;    c->qcount++;     sg = dequeue(&c->recvq);    if(sg != nil) {    gp = sg->g;    runtime·unlock(c);    if(sg->releasetime)    sg->releasetime = runtime·cputicks();    runtime·ready(gp);    } else    runtime·unlock(c);    if(pres != nil)    *pres = true;    if(mysg.releasetime > 0)    runtime·blockevent(mysg.releasetime - t0, 2);    return; closed:    runtime·unlock(c);    runtime·panicstring("send on closed channel");}
  1. 判斷隊列類型,非同步隊列則轉到5
  2. 從等待隊列中擷取等待隊列中的接受者
  3. 如果取到接受者,則將對象直接傳遞給接受者,然後喚醒接受者,發送過程完成
  4. 如果未取到接受者,則將寄件者enqueue到發送隊列,寄件者進入阻塞狀態
  5. 非同步隊列首先判斷隊列緩衝是否還有空間
  6. 如果緩衝空間已滿,則將寄件者enqueue到發送隊列,寄件者進入阻塞狀態
  7. 如果緩衝空間未滿,則將元素copy到緩衝中,這時寄件者就不會進入阻塞狀態
  8. 嘗試喚醒等待隊列中的一個接受者

chan接受

voidruntime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *received){    SudoG *sg;    SudoG mysg;    G *gp;    int64 t0;     if(debug)    runtime·printf("chanrecv: chan=%p\n", c);     if(c == nil) {    USED(t);    if(selected != nil) {    *selected = false;    return;    }    runtime·park(nil, nil, "chan receive (nil chan)");    return;  // not reached    }     t0 = 0;    mysg.releasetime = 0;    if(runtime·blockprofilerate > 0) {    t0 = runtime·cputicks();    mysg.releasetime = -1;    }     runtime·lock(c);    if(c->dataqsiz > 0)    goto asynch;     if(c->closed)    goto closed;     sg = dequeue(&c->sendq);    if(sg != nil) {    if(raceenabled)    racesync(c, sg);    runtime·unlock(c);     if(ep != nil)    c->elemalg->copy(c->elemsize, ep, sg->elem);    gp = sg->g;    gp->param = sg;    if(sg->releasetime)    sg->releasetime = runtime·cputicks();    runtime·ready(gp);     if(selected != nil)    *selected = true;    if(received != nil)    *received = true;    return;    }     if(selected != nil) {    runtime·unlock(c);    *selected = false;    return;    }     mysg.elem = ep;    mysg.g = g;    mysg.selgen = NOSELGEN;    g->param = nil;    enqueue(&c->recvq, &mysg);    runtime·park(runtime·unlock, c, "chan receive");     if(g->param == nil) {    runtime·lock(c);    if(!c->closed)    runtime·throw("chanrecv: spurious wakeup");    goto closed;    }     if(received != nil)    *received = true;    if(mysg.releasetime > 0)    runtime·blockevent(mysg.releasetime - t0, 2);    return; asynch:    if(c->qcount <= 0) {    if(c->closed)    goto closed;     if(selected != nil) {    runtime·unlock(c);    *selected = false;    if(received != nil)    *received = false;    return;    }    mysg.g = g;    mysg.elem = nil;    mysg.selgen = NOSELGEN;    enqueue(&c->recvq, &mysg);    runtime·park(runtime·unlock, c, "chan receive");     runtime·lock(c);    goto asynch;    }     if(raceenabled)    runtime·raceacquire(chanbuf(c, c->recvx));     if(ep != nil)    c->elemalg->copy(c->elemsize, ep, chanbuf(c, c->recvx));    c->elemalg->copy(c->elemsize, chanbuf(c, c->recvx), nil);    if(++c->recvx == c->dataqsiz)    c->recvx = 0;    c->qcount--;     sg = dequeue(&c->sendq);    if(sg != nil) {    gp = sg->g;    runtime·unlock(c);    if(sg->releasetime)    sg->releasetime = runtime·cputicks();    runtime·ready(gp);    } else    runtime·unlock(c);     if(selected != nil)    *selected = true;    if(received != nil)    *received = true;    if(mysg.releasetime > 0)    runtime·blockevent(mysg.releasetime - t0, 2);    return; closed:    if(ep != nil)    c->elemalg->copy(c->elemsize, ep, nil);    if(selected != nil)    *selected = true;    if(received != nil)    *received = false;    if(raceenabled)    runtime·raceacquire(c);    runtime·unlock(c);    if(mysg.releasetime > 0)    runtime·blockevent(mysg.releasetime - t0, 2);}
  1. 判斷隊列類型,如果是非同步隊列則轉到5
  2. 從發送隊列擷取接受者
  3. 如果取到接受者,則直接從接受者擷取元素,並喚醒寄件者,接受過程完成
  4. 如果未取到接受者,則將自身enqueue到等待隊列,阻塞goroutine等待寄件者喚醒
  5. 非同步隊列首先判斷隊列緩衝中是否有元素
  6. 緩衝為空白時,則將自身enqueue到等待隊列,阻塞goroutine等待寄件者喚醒
  7. 緩衝非空時,取出緩衝中的第一個元素
  8. 然後嘗試喚醒發送隊列中的一個寄件者

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.