溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Go語(yǔ)言中g(shù)oroutine怎么創(chuàng)建

發(fā)布時(shí)間:2023-01-14 10:00:45 來(lái)源:億速云 閱讀:121 作者:iii 欄目:編程語(yǔ)言

這篇“Go語(yǔ)言中g(shù)oroutine怎么創(chuàng)建”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“Go語(yǔ)言中g(shù)oroutine怎么創(chuàng)建”文章吧。

goroutine是Go語(yǔ)言中的輕量級(jí)線程實(shí)現(xiàn),是建立在線程之上的輕量級(jí)的抽象,由Go運(yùn)行時(shí)(runtime)管理。goroutine允許我們以非常低的代價(jià)在同一個(gè)地址空間中并行地執(zhí)行多個(gè)函數(shù)或者方法;相比于線程,它的創(chuàng)建和銷毀的代價(jià)要小很多,并且它的調(diào)度是獨(dú)立于線程的。

在編寫 Socket 網(wǎng)絡(luò)程序時(shí),需要提前準(zhǔn)備一個(gè)線程池為每一個(gè) Socket 的收發(fā)包分配一個(gè)線程。開發(fā)人員需要在線程數(shù)量和 CPU 數(shù)量間建立一個(gè)對(duì)應(yīng)關(guān)系,以保證每個(gè)任務(wù)能及時(shí)地被分配到 CPU 上進(jìn)行處理,同時(shí)避免多個(gè)任務(wù)頻繁地在線程間切換執(zhí)行而損失效率。

雖然,線程池為邏輯編寫者提供了線程分配的抽象機(jī)制。但是,如果面對(duì)隨時(shí)隨地可能發(fā)生的并發(fā)和線程處理需求,線程池就不是非常直觀和方便了。能否有一種機(jī)制:使用者分配足夠多的任務(wù),系統(tǒng)能自動(dòng)幫助使用者把任務(wù)分配到 CPU 上,讓這些任務(wù)盡量并發(fā)運(yùn)作。這種機(jī)制在 Go語(yǔ)言中被稱為 goroutine。

goroutine 是 Go語(yǔ)言中的輕量級(jí)線程實(shí)現(xiàn),由 Go 運(yùn)行時(shí)(runtime)管理。Go 程序會(huì)智能地將 goroutine 中的任務(wù)合理地分配給每個(gè) CPU。

Goroutine是建立在線程之上的輕量級(jí)的抽象。它允許我們以非常低的代價(jià)在同一個(gè)地址空間中并行地執(zhí)行多個(gè)函數(shù)或者方法。相比于線程,它的創(chuàng)建和銷毀的代價(jià)要小很多,并且它的調(diào)度是獨(dú)立于線程的。

Go 程序從 main 包的 main() 函數(shù)開始,在程序啟動(dòng)時(shí),Go 程序就會(huì)為 main() 函數(shù)創(chuàng)建一個(gè)默認(rèn)的 goroutine。

使用普通函數(shù)創(chuàng)建 goroutine

Go 程序中使用 go 關(guān)鍵字為一個(gè)函數(shù)創(chuàng)建一個(gè) goroutine。一個(gè)函數(shù)可以被創(chuàng)建多個(gè) goroutine,一個(gè) goroutine 必定對(duì)應(yīng)一個(gè)函數(shù)。

1) 格式

為一個(gè)普通函數(shù)創(chuàng)建 goroutine 的寫法如下:

go 函數(shù)名( 參數(shù)列表 )

  • 函數(shù)名:要調(diào)用的函數(shù)名。

  • 參數(shù)列表:調(diào)用函數(shù)需要傳入的參數(shù)。

使用 go 關(guān)鍵字創(chuàng)建 goroutine 時(shí),被調(diào)用函數(shù)的返回值會(huì)被忽略。

如果需要在 goroutine 中返回?cái)?shù)據(jù),請(qǐng)使用后面介紹的通道(channel)特性,通過(guò)通道把數(shù)據(jù)從 goroutine 中作為返回值傳出。

2) 例子

使用 go 關(guān)鍵字,將 running() 函數(shù)并發(fā)執(zhí)行,每隔一秒打印一次計(jì)數(shù)器,而 main 的 goroutine 則等待用戶輸入,兩個(gè)行為可以同時(shí)進(jìn)行。請(qǐng)參考下面代碼:

package main
import (
    "fmt"
    "time"
)
func running() {
    var times int
    // 構(gòu)建一個(gè)無(wú)限循環(huán)
    for {
        times++
        fmt.Println("tick", times)
        // 延時(shí)1秒
        time.Sleep(time.Second)
    }
}
func main() {
    // 并發(fā)執(zhí)行程序
    go running()
    // 接受命令行輸入, 不做任何事情
    var input string
    fmt.Scanln(&input)
}

命令行輸出如下:

Go語(yǔ)言中g(shù)oroutine怎么創(chuàng)建

代碼執(zhí)行后,命令行會(huì)不斷地輸出 tick,同時(shí)可以使用 fmt.Scanln() 接受用戶輸入。兩個(gè)環(huán)節(jié)可以同時(shí)進(jìn)行。

代碼說(shuō)明如下:

  • 第 12 行,使用 for 形成一個(gè)無(wú)限循環(huán)。

  • 第 13 行,times 變量在循環(huán)中不斷自增。

  • 第 14 行,輸出 times 變量的值。

  • 第 17 行,使用 time.Sleep 暫停 1 秒后繼續(xù)循環(huán)。

  • 第 25 行,使用 go 關(guān)鍵字讓 running() 函數(shù)并發(fā)運(yùn)行。

  • 第 29 行,接受用戶輸入,直到按 Enter 鍵時(shí)將輸入的內(nèi)容寫入 input 變量中并返回,整個(gè)程序終止。

這段代碼的執(zhí)行順序如下圖所示。

Go語(yǔ)言中g(shù)oroutine怎么創(chuàng)建
圖:并發(fā)運(yùn)行圖

這個(gè)例子中,Go 程序在啟動(dòng)時(shí),運(yùn)行時(shí)(runtime)會(huì)默認(rèn)為 main() 函數(shù)創(chuàng)建一個(gè) goroutine。在 main() 函數(shù)的 goroutine 中執(zhí)行到 go running 語(yǔ)句時(shí),歸屬于 running() 函數(shù)的 goroutine 被創(chuàng)建,running() 函數(shù)開始在自己的 goroutine 中執(zhí)行。此時(shí),main() 繼續(xù)執(zhí)行,兩個(gè) goroutine 通過(guò) Go 程序的調(diào)度機(jī)制同時(shí)運(yùn)作。

使用匿名函數(shù)創(chuàng)建goroutine

go 關(guān)鍵字后也可以為匿名函數(shù)或閉包啟動(dòng) goroutine。

1) 使用匿名函數(shù)創(chuàng)建goroutine的格式

使用匿名函數(shù)或閉包創(chuàng)建 goroutine 時(shí),除了將函數(shù)定義部分寫在 go 的后面之外,還需要加上匿名函數(shù)的調(diào)用參數(shù),格式如下:

go func( 參數(shù)列表 ){
    函數(shù)體
}( 調(diào)用參數(shù)列表 )

其中:

  • 參數(shù)列表:函數(shù)體內(nèi)的參數(shù)變量列表。

  • 函數(shù)體:匿名函數(shù)的代碼。

  • 調(diào)用參數(shù)列表:?jiǎn)?dòng) goroutine 時(shí),需要向匿名函數(shù)傳遞的調(diào)用參數(shù)。

2) 使用匿名函數(shù)創(chuàng)建goroutine的例子

在 main() 函數(shù)中創(chuàng)建一個(gè)匿名函數(shù)并為匿名函數(shù)啟動(dòng) goroutine。匿名函數(shù)沒有參數(shù)。代碼將并行執(zhí)行定時(shí)打印計(jì)數(shù)的效果。參見下面的代碼:

package main
import (
    "fmt"
    "time"
)
func main() {
    go func() {
        var times int
        for {
            times++
            fmt.Println("tick", times)
            time.Sleep(time.Second)
        }
    }()
    var input string
    fmt.Scanln(&input)
}

代碼說(shuō)明如下:

  • 第 10 行,go 后面接匿名函數(shù)啟動(dòng) goroutine。

  • 第 12~19 行的邏輯與前面程序的 running() 函數(shù)一致。

  • 第 21 行的括號(hào)的功能是調(diào)用匿名函數(shù)的參數(shù)列表。由于第 10 行的匿名函數(shù)沒有參數(shù),因此第 21 行的參數(shù)列表也是空的。

擴(kuò)展知識(shí):Goroutine與線程的區(qū)別

許多人認(rèn)為goroutine比線程運(yùn)行得更快,這是一個(gè)誤解。Goroutine并不會(huì)更快,它只是增加了更多的并發(fā)性。當(dāng)一個(gè)goroutine被阻塞(比如等待IO),golang的scheduler會(huì)調(diào)度其他可以執(zhí)行的goroutine運(yùn)行。與線程相比,它有以下的幾個(gè)優(yōu)點(diǎn):

內(nèi)存消耗更少:

Goroutine所需要的內(nèi)存通常只有2kb,而線程則需要1Mb(500倍)

創(chuàng)建與銷毀的開銷更?。?/strong>

由于線程創(chuàng)建時(shí)需要向操作系統(tǒng)申請(qǐng)資源,并且在銷毀時(shí)將資源歸還,因此它的創(chuàng)建和銷毀的開銷比較大。相比之下,goroutine的創(chuàng)建和銷毀是由go語(yǔ)言在運(yùn)行時(shí)自己管理的,因此開銷更低。

切換開銷更?。?/strong>

只是goroutine之于線程的主要區(qū)別,也是golang能夠?qū)崿F(xiàn)高并發(fā)的主要原因。線程的調(diào)度方式是搶占式的,如果一個(gè)線程的執(zhí)行時(shí)間超過(guò)了分配給它的時(shí)間片,就會(huì)被其他可執(zhí)行的線程搶占。在線程切換的過(guò)程中需要保存/恢復(fù)所有的寄存器信息,比如16個(gè)通用寄存器,PC(Program Counter)、SP(Stack Pointer)段寄存器等等。而goroutine的調(diào)度是協(xié)同式的,它不會(huì)直接地與操作系統(tǒng)內(nèi)核打交道。當(dāng)goroutine進(jìn)行切換的時(shí)候,之后很少量的寄存器需要保存和恢復(fù)(PC和SP)。因此goroutine的切換效率更高。

以上就是關(guān)于“Go語(yǔ)言中g(shù)oroutine怎么創(chuàng)建”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI