go語言調度器

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

調度器就是將goroutine分配到背景工作執行緒中運行
涉及3種類型的對象:
G - goroutine
M - 背景工作執行緒即os線程
P - 處理器,一種用來運行go代碼的抽象資源,最大數目不能超過GOMAXPROCS,在運行go代碼時需要關聯一個M

全域的運行隊列:

G *runtime·sched.runqhead;G *runtime·sched.runqtail;int runtime·sched.runqsize;

P結構的主要成員:包含一個本地運行隊列

struct P{    uint32status;// one of Pidle/Prunning/...    uint32schedtick;// incremented on every scheduler call     M*m;// back-link to associated M (nil if idle)     // Queue of runnable goroutines.    uint32runqhead;    uint32runqtail;    G*runq[256];};

schedule函數主要部分代碼

// One round of scheduler: find a runnable goroutine and execute it.// Never returns.static voidschedule(void){    G *gp;    uint32 tick;     ......top:    ......        gp = nil;    // Check the global runnable queue once in a while to ensure fairness.    // Otherwise two goroutines can completely occupy the local runqueue    // by constantly respawning each other.    tick = g->m->p->schedtick;    // This is a fancy way to say tick%61==0,    // it uses 2 MUL instructions instead of a single DIV and so is faster on modern processors.    if(tick - (((uint64)tick*0x4325c53fu)>>36)*61 == 0 && runtime·sched.runqsize > 0) {    runtime·lock(&runtime·sched.lock);    gp = globrunqget(g->m->p, 1);    runtime·unlock(&runtime·sched.lock);    if(gp)    resetspinning();    }    if(gp == nil) {    gp = runqget(g->m->p);    if(gp && g->m->spinning)    runtime·throw("schedule: spinning with local work");    }    if(gp == nil) {    gp = findrunnable();  // blocks until work is available    resetspinning();    }     execute(gp);}

調度器在超過一定間隔時間的情況下,為了公平原則,首先會從全域的運行隊列擷取G
從本地的運行隊列中擷取G
等待新的G進入運行隊列

globrunqget從全域運行隊列擷取G,同時它還會將一定數量的G轉移到P的本地運行隊列中.

runqget從本地運行隊列擷取G,本地運行隊列的實現是無鎖的:

// Get g from local runnable queue.// Executed only by the owner P.static G*runqget(P *p){    G *gp;    uint32 t, h;     for(;;) {    h = runtime·atomicload(&p->runqhead);  // load-acquire, synchronize with other consumers    t = p->runqtail;    if(t == h)    return nil;    gp = p->runq[h%nelem(p->runq)];    if(runtime·cas(&p->runqhead, h, h+1))  // cas-release, commits consume    return gp;    }}

findrunnable阻塞等待可啟動並執行G

  • 檢查本地運行隊列
  • 檢查全域運行隊列
  • 以non-blocking的模式poll network
  • 檢查其它P的本地運行隊列
  • 如果最後依舊無法在系統內擷取到G,那麼就以blocking的模式poll network

    // Finds a runnable goroutine to execute.
    // Tries to steal from other P's, get g from global queue, poll network.
    static G
    findrunnable(void)
    {
    G
    gp;
    P *p;
    int32 i;

    top:
    if(runtime·sched.gcwaiting) {
    gcstopm();
    goto top;
    }
    if(runtime·fingwait && runtime·fingwake && (gp = runtime·wakefing()) != nil)
    runtime·ready(gp);
    // local runq
    gp = runqget(g->m->p);
    if(gp)
    return gp;
    // global runq
    if(runtime·sched.runqsize) {
    runtime·lock(&runtime·sched.lock);
    gp = globrunqget(g->m->p, 0);
    runtime·unlock(&runtime·sched.lock);
    if(gp)
    return gp;
    }
    // poll network
    gp = runtime·netpoll(false); // non-blocking
    if(gp) {
    injectglist(gp->schedlink);
    runtime·casgstatus(gp, Gwaiting, Grunnable);
    return gp;
    }
    // If number of spinning M's >= number of busy P's, block.
    // This is necessary to prevent excessive CPU consumption
    // when GOMAXPROCS>>1 but the program parallelism is low.
    if(!g->m->spinning && 2 * runtime·atomicload(&runtime·sched.nmspinning) >= runtime·gomaxprocs - runtime·atomicload(&runtime·sched.npidle)) // TODO: fast atomic
    goto stop;
    if(!g->m->spinning) {
    g->m->spinning = true;
    runtime·xadd(&runtime·sched.nmspinning, 1);
    }
    // random steal from other P's
    for(i = 0; i < 2*runtime·gomaxprocs; i++) {
    if(runtime·sched.gcwaiting)
    goto top;
    p = runtime·allp[runtime·fastrand1()%runtime·gomaxprocs];
    if(p == g->m->p)
    gp = runqget(p);
    else
    gp = runqsteal(g->m->p, p);
    if(gp)
    return gp;
    }
    stop:
    // return P and block
    runtime·lock(&runtime·sched.lock);
    if(runtime·sched.gcwaiting) {
    runtime·unlock(&runtime·sched.lock);
    goto top;
    }
    if(runtime·sched.runqsize) {
    gp = globrunqget(g->m->p, 0);
    runtime·unlock(&runtime·sched.lock);
    return gp;
    }
    p = releasep();
    pidleput(p);
    runtime·unlock(&runtime·sched.lock);
    if(g->m->spinning) {
    g->m->spinning = false;
    runtime·xadd(&runtime·sched.nmspinning, -1);
    }
    // check all runqueues once again
    for(i = 0; i < runtime·gomaxprocs; i++) {
    p = runtime·allp[i];
    if(p && p->runqhead != p->runqtail) {
    runtime·lock(&runtime·sched.lock);
    p = pidleget();
    runtime·unlock(&runtime·sched.lock);
    if(p) {
    acquirep(p);
    goto top;
    }
    break;
    }
    }
    // poll network
    if(runtime·xchg64(&runtime·sched.lastpoll, 0) != 0) {
    if(g->m->p)
    runtime·throw("findrunnable: netpoll with p");
    if(g->m->spinning)
    runtime·throw("findrunnable: netpoll with spinning");
    gp = runtime·netpoll(true); // block until new work is available
    runtime·atomicstore64(&runtime·sched.lastpoll, runtime·nanotime());
    if(gp) {
    runtime·lock(&runtime·sched.lock);
    p = pidleget();
    runtime·unlock(&runtime·sched.lock);
    if(p) {
    acquirep(p);
    injectglist(gp->schedlink);
    runtime·casgstatus(gp, Gwaiting, Grunnable);
    return gp;
    }
    injectglist(gp);
    }
    }
    stopm();
    goto top;
    }

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.