進程(Process),執行緒(Thread),協程(Coroutine,也叫輕量級執行緒)
進程進程是一個程式在一個數據集中的一次動態執行過程,可以簡單理解爲「正在執行的程式」,它是CPU資源分配和排程的獨立單位。 進程一般由程式、數據集、過程控制塊三部分組成。我們編寫的程式用來描述進程要完成哪些功能以及如何完成;數據集則是程式在執行過程中所需要使用的資源;過程控制塊用來記錄進程的外部特徵,描述進程的執行變化過程,系統可以利用它來控制和管理進程,它是系統感知進程存在的唯一標誌。 進程的侷限是建立、復原和切換的開銷比較大。
執行緒執行緒是在進程之後發展出來的概念。 執行緒也叫輕量級進程,它是一個基本的CPU執行單元,也是程式執行過程中的最小單元,由執行緒ID、程式計數器、暫存器集合和堆疊共同組成。一個進程可以包含多個執行緒。 執行緒的優點是減小了程式併發執行時的開銷,提高了操作系統的併發效能,缺點是執行緒沒有自己的系統資源,只擁有在執行時必不可少的資源,但同一進程的各執行緒可以共用進程所擁有的系統資源,如果把進程比作一個車間,那麼執行緒就好比是車間裏面的工人。不過對於某些獨佔性資源存在鎖機制 機製,處理不當可能會產生「死鎖」。
協程協程是一種使用者態的輕量級執行緒,又稱微執行緒,英文名Coroutine,協程的排程完全由使用者控制。人們通常將協程和子程式(函數)比較着理解。 子程式呼叫總是一個入口,一次返回,一旦退出即完成了子程式的執行。
與傳統的系統級執行緒和進程相比,協程的最大優勢在於其"輕量級",可以輕鬆建立上百萬個而不會導致系統資源衰竭,而執行緒和進程通常最多也不能超過1萬的。這也是協程也叫輕量級執行緒的原因。
協程的特點在於是一個執行緒執行,與多執行緒相比,其優勢體現在:協程的執行效率極高。因爲子程式切換不是執行緒切換,而是由程式自身控制,因此,沒有執行緒切換的開銷,和多執行緒比,執行緒數量越多,協程的效能優勢就越明顯。
go中使用Goroutine來實現併發concurrently。
Goroutine是Go語言特有的名詞。區別於進程Process,執行緒Thread,協程Coroutine,因爲Go語言的創造者們覺得和他們是有所區別的,所以專門創造了Goroutine。
Goroutine是與其他函數或方法同時執行的函數或方法。Goroutines可以被認爲是輕量級的執行緒。與執行緒相比,建立Goroutine的成本很小,它就是一段程式碼,一個函數入口。以及在堆上爲其分配的一個堆疊(初始大小爲4K,會隨着程式的執行自動增長刪除)。因此它非常廉價,Go應用程式可以併發執行數千個Goroutines。
Goroutines線上程上的優勢。
封裝main函數的goroutine稱爲主goroutine。
主goroutine所做的事情並不是執行main函數那麼簡單。它首先要做的是:設定每一個goroutine所能申請的棧空間的最大尺寸。在32位元的計算機系統中此最大尺寸爲250MB,而在64位元的計算機系統中此尺寸爲1GB。如果有某個goroutine的棧空間尺寸大於這個限制,那麼執行時系統就會引發一個棧溢位(stack overflow)的執行時恐慌。隨後,這個go程式的執行也會終止。
此後,主goroutine會進行一系列的初始化工作,涉及的工作內容大致如下:
在函數或方法呼叫前面加上關鍵字go,您將會同時執行一個新的Goroutine。
範例程式碼:
package main
import (
"fmt"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
fmt.Println("main function")
}
執行結果:可能會值輸出「main function」。
我們開始的Goroutine怎麼樣了?我們需要瞭解Goroutine的規則
修改以上程式碼:
package main
import (
"fmt"
"time"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
time.Sleep(1 * time.Second)
fmt.Println("main function")
}
執行結果:
在上面的程式中,我們已經呼叫了時間包的Sleep方法,它會在執行過程中睡覺。在這種情況下,main的goroutine被用來睡覺1秒。現在呼叫go hello()有足夠的時間在main Goroutine終止之前執行。這個程式首先列印Hello world goroutine,等待1秒,然後列印main函數。
範例程式碼:
package main
import (
"fmt"
"time"
)
func numbers() {
for i := 1; i <= 5; i++ {
time.Sleep(250 * time.Millisecond)
fmt.Printf("%d ", i)
}
}
func alphabets() {
for i := 'a'; i <= 'e'; i++ {
time.Sleep(400 * time.Millisecond)
fmt.Printf("%c ", i)
}
}
func main() {
go numbers()
go alphabets()
time.Sleep(3000 * time.Millisecond)
fmt.Println("main terminated")
}
執行結果:
1 a 2 3 b 4 c 5 d e main terminated
時間軸分析: