您好,登錄后才能下訂單哦!
這篇“Go語言中goroutine怎么創建”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Go語言中goroutine怎么創建”文章吧。
goroutine是Go語言中的輕量級線程實現,是建立在線程之上的輕量級的抽象,由Go運行時(runtime)管理。goroutine允許我們以非常低的代價在同一個地址空間中并行地執行多個函數或者方法;相比于線程,它的創建和銷毀的代價要小很多,并且它的調度是獨立于線程的。
在編寫 Socket 網絡程序時,需要提前準備一個線程池為每一個 Socket 的收發包分配一個線程。開發人員需要在線程數量和 CPU 數量間建立一個對應關系,以保證每個任務能及時地被分配到 CPU 上進行處理,同時避免多個任務頻繁地在線程間切換執行而損失效率。
雖然,線程池為邏輯編寫者提供了線程分配的抽象機制。但是,如果面對隨時隨地可能發生的并發和線程處理需求,線程池就不是非常直觀和方便了。能否有一種機制:使用者分配足夠多的任務,系統能自動幫助使用者把任務分配到 CPU 上,讓這些任務盡量并發運作。這種機制在 Go語言中被稱為 goroutine。
goroutine 是 Go語言中的輕量級線程實現,由 Go 運行時(runtime)管理。Go 程序會智能地將 goroutine 中的任務合理地分配給每個 CPU。
Goroutine是建立在線程之上的輕量級的抽象。它允許我們以非常低的代價在同一個地址空間中并行地執行多個函數或者方法。相比于線程,它的創建和銷毀的代價要小很多,并且它的調度是獨立于線程的。
Go 程序從 main 包的 main() 函數開始,在程序啟動時,Go 程序就會為 main() 函數創建一個默認的 goroutine。
使用普通函數創建 goroutine
Go 程序中使用 go 關鍵字為一個函數創建一個 goroutine。一個函數可以被創建多個 goroutine,一個 goroutine 必定對應一個函數。
1) 格式
為一個普通函數創建 goroutine 的寫法如下:
go 函數名( 參數列表 )
函數名:要調用的函數名。
參數列表:調用函數需要傳入的參數。
使用 go 關鍵字創建 goroutine 時,被調用函數的返回值會被忽略。
如果需要在 goroutine 中返回數據,請使用后面介紹的通道(channel)特性,通過通道把數據從 goroutine 中作為返回值傳出。
2) 例子
使用 go 關鍵字,將 running() 函數并發執行,每隔一秒打印一次計數器,而 main 的 goroutine 則等待用戶輸入,兩個行為可以同時進行。請參考下面代碼:
package main
import (
"fmt"
"time"
)
func running() {
var times int
// 構建一個無限循環
for {
times++
fmt.Println("tick", times)
// 延時1秒
time.Sleep(time.Second)
}
}
func main() {
// 并發執行程序
go running()
// 接受命令行輸入, 不做任何事情
var input string
fmt.Scanln(&input)
}
命令行輸出如下:
代碼執行后,命令行會不斷地輸出 tick,同時可以使用 fmt.Scanln() 接受用戶輸入。兩個環節可以同時進行。
代碼說明如下:
第 12 行,使用 for 形成一個無限循環。
第 13 行,times 變量在循環中不斷自增。
第 14 行,輸出 times 變量的值。
第 17 行,使用 time.Sleep 暫停 1 秒后繼續循環。
第 25 行,使用 go 關鍵字讓 running() 函數并發運行。
第 29 行,接受用戶輸入,直到按 Enter 鍵時將輸入的內容寫入 input 變量中并返回,整個程序終止。
這段代碼的執行順序如下圖所示。
圖:并發運行圖
這個例子中,Go 程序在啟動時,運行時(runtime)會默認為 main() 函數創建一個 goroutine。在 main() 函數的 goroutine 中執行到 go running 語句時,歸屬于 running() 函數的 goroutine 被創建,running() 函數開始在自己的 goroutine 中執行。此時,main() 繼續執行,兩個 goroutine 通過 Go 程序的調度機制同時運作。
使用匿名函數創建goroutine
go 關鍵字后也可以為匿名函數或閉包啟動 goroutine。
1) 使用匿名函數創建goroutine的格式
使用匿名函數或閉包創建 goroutine 時,除了將函數定義部分寫在 go 的后面之外,還需要加上匿名函數的調用參數,格式如下:
go func( 參數列表 ){
函數體
}( 調用參數列表 )
其中:
參數列表:函數體內的參數變量列表。
函數體:匿名函數的代碼。
調用參數列表:啟動 goroutine 時,需要向匿名函數傳遞的調用參數。
2) 使用匿名函數創建goroutine的例子
在 main() 函數中創建一個匿名函數并為匿名函數啟動 goroutine。匿名函數沒有參數。代碼將并行執行定時打印計數的效果。參見下面的代碼:
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)
}
代碼說明如下:
第 10 行,go 后面接匿名函數啟動 goroutine。
第 12~19 行的邏輯與前面程序的 running() 函數一致。
第 21 行的括號的功能是調用匿名函數的參數列表。由于第 10 行的匿名函數沒有參數,因此第 21 行的參數列表也是空的。
擴展知識:Goroutine與線程的區別
許多人認為goroutine比線程運行得更快,這是一個誤解。Goroutine并不會更快,它只是增加了更多的并發性。當一個goroutine被阻塞(比如等待IO),golang的scheduler會調度其他可以執行的goroutine運行。與線程相比,它有以下的幾個優點:
內存消耗更少:
Goroutine所需要的內存通常只有2kb,而線程則需要1Mb(500倍)
創建與銷毀的開銷更小:
由于線程創建時需要向操作系統申請資源,并且在銷毀時將資源歸還,因此它的創建和銷毀的開銷比較大。相比之下,goroutine的創建和銷毀是由go語言在運行時自己管理的,因此開銷更低。
切換開銷更小:
只是goroutine之于線程的主要區別,也是golang能夠實現高并發的主要原因。線程的調度方式是搶占式的,如果一個線程的執行時間超過了分配給它的時間片,就會被其他可執行的線程搶占。在線程切換的過程中需要保存/恢復所有的寄存器信息,比如16個通用寄存器,PC(Program Counter)、SP(Stack Pointer)段寄存器等等。而goroutine的調度是協同式的,它不會直接地與操作系統內核打交道。當goroutine進行切換的時候,之后很少量的寄存器需要保存和恢復(PC和SP)。因此goroutine的切換效率更高。
以上就是關于“Go語言中goroutine怎么創建”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。