Go語言 關(guān)鍵字:go

2018-07-25 17:23 更新

在Go語言中,表達(dá)式go f(x, y, z)會啟動一個新的goroutine運行函數(shù)f(x, y, z)。函數(shù)f,變量x、y、z的值是在原goroutine計算的,只有函數(shù)f的執(zhí)行是在新的goroutine中的。顯然,新的goroutine不能和當(dāng)前go線程用同一個棧,否則會相互覆蓋。所以對go關(guān)鍵字的調(diào)用協(xié)議與普通函數(shù)調(diào)用是不同的。

首先,讓我們看一下如果是C代碼新建一條線程的實現(xiàn)會是什么樣子的。大概會先建一個結(jié)構(gòu)體,結(jié)構(gòu)體里存f、x、y和z的值。然后寫一個help函數(shù),將這個結(jié)構(gòu)體指針作為輸入,函數(shù)體內(nèi)調(diào)用f(x, y, z)。接下來,先填充結(jié)構(gòu)體,然后調(diào)用newThread(help, structptr)。其中help是剛剛那個函數(shù),它會調(diào)用f(x, y, z)。help函數(shù)將作為所有新建線程的入口函數(shù)。

這樣做有什么問題么?沒什么問題...只是這樣實現(xiàn)代價有點高,每次調(diào)用都會花上不少的指令。其實Go語言中對go關(guān)鍵字的實現(xiàn)會更加hack一些,避免了這么做。

先看看正常的函數(shù)調(diào)用,下面是調(diào)用f(1, 2, 3)時的匯編代碼:

    MOVL    $1, 0(SP)
    MOVL    $2, 4(SP)
    MOVL    $3, 8(SP)
    CALL    f(SB)

首先將參數(shù)1、2、3進棧,然后調(diào)用函數(shù)f。

下面是go f(1, 2, 3)生成的代碼:

    MOVL    $1, 0(SP)
    MOVL    $2, 4(SP)
    MOVL    $3, 8(SP)
    PUSHQ   $f(SB)
    PUSHQ   $12
    CALL    runtime.newproc(SB)
    POPQ    AX
    POPQ    AX

對比一個會發(fā)現(xiàn),前面部分跟普通函數(shù)調(diào)用是一樣的,將參數(shù)存儲在正常的位置,并沒有新建一個輔助的結(jié)構(gòu)體。接下來的兩條指令有些不同,將f和12作為參數(shù)進棧而不直接調(diào)用f,然后調(diào)用函數(shù)runtime.newproc。

12是參數(shù)占用的大小。runtime.newproc函數(shù)接受的參數(shù)分別是:參數(shù)大小,新的goroutine是要運行的函數(shù),函數(shù)的n個參數(shù)。

runtime.newproc中,會新建一個??臻g,將棧參數(shù)的12個字節(jié)拷貝到新棧空間中并讓棧指針指向參數(shù)。這時的線程狀態(tài)有點像當(dāng)被調(diào)度器剝奪CPU后一樣,寄存器PC、SP會被保存到類似于進程控制塊的一個結(jié)構(gòu)體struct G內(nèi)。f被存放在了struct G的entry域,后面進行調(diào)度器恢復(fù)goroutine的運行,新線程將從f開始執(zhí)行。

和前面說的如果用C實現(xiàn)的差別就在于,沒有使用輔助的結(jié)構(gòu)體,而runtime.newproc實際上就是help函數(shù)。在函數(shù)協(xié)議上,go表達(dá)式調(diào)用就比普通的函數(shù)調(diào)用多四條指令而已,并且在實際上并沒有為go關(guān)鍵字設(shè)計一套特殊的東西。不得不說這個做法真的非常精妙!

總結(jié)一個,go關(guān)鍵字的實現(xiàn)僅僅是一個語法糖衣而已,也就是:

    go f(args)

可以看作

    runtime.newproc(size, f, args)

links


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號