#這篇文章將繼續關注 Go 語言基礎部分。我們將討論關於效能方面的一些知識,並透過創建一些簡單的 goroutine 來擴展我們的應用程式。
我們也會專注於一些 Go 語言的底層執行邏輯以及 Go 語言與其他語言的差異。
在繼續討論之前,我們必須先理解並發與並行的概念。 Golang 可以實現並發和並行。
我們一起來看下並發與並行的差異。
應用程式可能會透過處理多個進程來完成預期的功能。我們來假設一個簡單的電子商務網站,經評估有下列需要並發執行的任務:
#在網頁的頂部顯示最新的交易和產品資訊;
顯示網站目前的線上使用者數量;
#當使用者選擇商品後更新購物車詳情;
為「目標交易額」倒數計時;
該網站需要同時執行所有這些任務,以使用戶與網站保持關聯,並使網站對用戶有吸引力並吸引更多業務。
因此,為了滿足業務需要,一個簡單的應用程式或網站都可能包含一組後台運行的任務。
上圖所示的兩個範例中,有多個任務同時執行,但是它們之間仍然有差異。讓我們進一步研究以便能更了解。
假設這樣一個場景,我們有一台單核心機器,需要完成多個任務,但有個限制,在任何時刻,單核心機器上只能運行一個任務。
在並發模型中,任務之間存在上下文切換。程式正在處理多個任務,但由於我們只有單核,因此任務無法一起執行。
任務之間的上下文切換很快,以至於我們感覺任務是同時運行的。
在執行過程中沒有並行執行的因素,因為是單核心系統,多行程不能並行執行。
如上圖所示,Concurrency (Without Parallelism) 有兩個任務需要並發執行。在任何時候,只有一個任務在運行並且任務之間存在上下文切換。
使用單一核心的情況下,存在核數限制。如果我們為機器增加核數,就可以在不同的核心上同時執行任務。
在上圖中(Parallelism),任一時刻都有兩個任務在執行,這兩個任務運行在不同的核心上。
並發是某一時間段內同時處理多個任務,並行是在某一時間點能執行多個任務。
使用 Go 語言可以輕鬆地將程式從並發擴展為並行執行。
使用 Go 語言實作並發和並行,我們需要了解協程(Goroutines)的概念。 Go 語言的協程可以理解為執行緒之上的一個包裝器,由 Go 運行時管理而不是作業系統。
Go 運行時負責給協程分配和回收資源,協程與完成多任務的執行緒非常相似但又比作業系統執行緒消耗更少的資源。協程與線程之間並非一對一的關係。
我們可以將應用程式「拆解」成多個並發任務,這些任務可以由不同的goroutine 完成,透過這種方式即可實現了Go 語言並發。
協程的優點:
更輕量級;
##容易擴展;#
虛擬執行緒;
#需要更少的初始記憶體(2KB);
#有必要的話,Go 運行時能分配更多的記憶體;
一起來看下一個簡單的例子:
package main import ( "fmt" "time" ) func main() { start := time.Now() func() { for i:=0; i < 3; i++ { fmt.Println(i) } }() func() { for i:=0; i < 3; i++ { fmt.Println(i) } }() elapsedTime := time.Since(start) fmt.Println("Total Time For Execution: " + elapsedTime.String()) time.Sleep(time.Second) }
上面的程式碼依序在main 函數裡面執行了兩個獨立函數。
程式碼沒有使用協程,程式在同一個執行緒中執行完成。程式沒有任何的並發性,執行結果如下:
程式碼依序執行,從主函數開始,先執行第一個函數,再執行第二個函數,最後從主函數正常退出。
上面的場景範例中沒有使用任何的協程。我們可以在執行函數之前使用 go 關鍵字開啟協程。
依舊是上面的例子,我們一起來看看使用go 關鍵字開啟協程之後會是什麼樣的:
package main import ( "fmt" "time" ) func main() { start := time.Now() go func() { for i:=0; i < 3; i++ { fmt.Println(i) } }() go func() { for i:=0; i < 3; i++ { fmt.Println(i) } }() elapsedTime := time.Since(start) fmt.Println("Total Time For Execution: " + elapsedTime.String()) time.Sleep(time.Second) }
執行上面的程式碼輸出:
上面的程式碼,使用go 關鍵字分別開啟了兩個協程並執行各自的函數,包括主協程,總共有3 個協程。
上面的程式碼,我們使用go 關鍵字開啟協程,函數會在協程中完成執行,而不是在主協程中執行,這樣增加了並發並提高了程式效能。
#Go 語言裡,可以透過下面這行簡單的程式碼設定程式執行的核心數目(PS :從Go 1.5開始,Go 的GOMAXPROCS 預設值已經設定為CPU 的核數)。
runtime.GOMAXPROCS(4)
這可以指定程式在多核心機器上運行,上面一行程式碼指定程式可以使用四個核心來執行。
一旦建立了協程,便可以在不同的核心中執行,從而實現並行並加快程式執行速度。
package main import ( "fmt" "time" "runtime" ) func main() { runtime.GOMAXPROCS(4) start := time.Now() go func() { for i:=0; i < 3; i++ { fmt.Println(i) } }() go func() { for i:=0; i < 3; i++ { fmt.Println(i) } }() elapsedTime := time.Since(start) fmt.Println("Total Time For Execution: " + elapsedTime.String()) time.Sleep(time.Second) }
上面的程式碼輸出如下:
使用 Go 語言可以輕鬆實現並發和並行。只需在函數之前添加 go 關鍵字即可提高程式執行速度。
以上是Go 基礎之 Goroutine的詳細內容。更多資訊請關注PHP中文網其他相關文章!