協程 進程和線程 進程 當運行一個應用程式的時候,操作系統會為這個應用程式啟動一個進程。可以將這個進程看作一個包含了應用程式在運行中需要用到和維護的各種資源的容器。這些資源包括但不限於記憶體地址空間、文件和設備的句柄以及線程 線程 一個線程是一個執行空間,這個空間會被操作系統調度來運行函數中所 ...
協程
進程和線程
進程
當運行一個應用程式的時候,操作系統會為這個應用程式啟動一個進程。可以將這個進程看作一個包含了應用程式在運行中需要用到和維護的各種資源的容器。這些資源包括但不限於記憶體地址空間、文件和設備的句柄以及線程
線程
一個線程是一個執行空間,這個空間會被操作系統調度來運行函數中所寫的代碼。每個進程至少包含一個線程,每個進程的初始線程被稱作主線程。因為執行這個線程的空間是應用程式的本身的空間,所以當主線程終止時,應用程式也會終止。操作系統將線程調度到某個處理器上運行,這個處理器並不一定是進程所在的處理器
併發和並行
併發
併發是指在一個邏輯處理器同時管理很多事情,這些事情可能只做了一半就被暫停去做別的事情了,golang的併發通過切換多個線程打到減少物理處理器空閑等待的目的
並行
並行是讓不同的代碼片段同時在不同的物理處理器上執行,大多數情況下,併發的效果比並行好,因為操作系統的硬體的總資源一般很少,但能支持系統同時做很多事情
goroutine協程
在一個協程中運行函數
在一個協程中運行一個函數其實很簡單,只要在函數調用前加上go即可
// 隨便寫一個demo函數
func Demo01(num int, contain string) {
for i := 0; i < num; i++ {
fmt.Println("這是一個goroutine", contain)
}
}
// 在協程中運行此函數
func main() {
go Demo01(10,"調用Demo函數")
fmt.Println("over!")
}
//輸出:
// over!
上述代碼由於demo調用在協程中進行,所以並不影響主函數的運行,當主函數運行結束之後,代碼運行直接停止,所以協程並沒有輸出任何內容,因此我們可以使用sleep方法讓主函數進入等待狀態
// 隨便寫一個demo函數
func Demo01(num int, contain string) {
for i := 0; i < num; i++ {
fmt.Println("這是一個goroutine", contain)
}
}
// 在協程中運行此函數
func main() {
go Demo01(10,"調用Demo函數")
time.Sleep(1000000000)
fmt.Println("over!")
}
//輸出:
// 這是一個goroutine demo調用
// 這是一個goroutine demo調用
// 這是一個goroutine demo調用
// 這是一個goroutine demo調用
// 這是一個goroutine demo調用
// over!
但是這樣子讓主函數等待協程運行,聽起來是個蠻蠢的想法,我們可以使用一個另一種方式解決問題,Waitgroup
方法
waitgroup方法
我們可以在使用waitgroup方法先實例化一個計數器,wg,在協程中使用wg.done進行操作計數器,從而達到主函數等待協程運行的一個目的
// demo方法
// 同時因為我們要對wg進行操作,因此要把wg的指針傳遞給函數
func Demo01(num int, contain string, wg *sync.WaitGroup) {
// 因為wg,done一般來說都在函數最後使用,因此我們把他放在defer延遲處理函數裡面
defer wg.Done()
for i := 0; i < num; i++ {
fmt.Println("這是一個goroutine", contain)
time.Sleep(1000000000)
}
}
// main方法
func main() {
// 實例化計數器wg
var wg sync.WaitGroup
// 要運行兩個goroutine函數
wg.Add(2)
go functions.Demo01(2, "the first goroutine!", &wg)
go functions.Demo01(3, "the second goroutine!", &wg)
// 等待兩個goroutine運行完畢
wg.Wait()
// 執行完畢後再輸出over!
fmt.Println("over!")
}
// 輸出:
// 這是一個goroutine the second goroutine!
// 這是一個goroutine the first goroutine!
// 這是一個goroutine the first goroutine!
// 這是一個goroutine the second goroutine!
// 這是一個goroutine the second goroutine!
// over!
併發執行
前文說過,併發執行就是讓協程運行在同一個邏輯處理器上,我們可以在主方法中使用runtime.GOMAXPROCS(1)
強制控制在一個邏輯處理器中
// main方法
func main() {
routine.GOMAXPROCS(1)
// 實例化計數器wg
var wg sync.WaitGroup
// 要運行兩個goroutine函數
wg.Add(2)
go functions.Demo01(2, "the first goroutine!", &wg)
go functions.Demo01(3, "the second goroutine!", &wg)
// 等待兩個goroutine運行完畢
wg.Wait()
// 執行完畢後再輸出over!
fmt.Println("over!")
}
這樣子會讓處理器在兩個工作中不斷的切換,而並不是真正的同時進行,而真正的同進行,需要並行執行
並行執行
並行執行就是讓各個goroutine都在單獨的邏輯處理器中運行,這時runtime.GOMAXPROCS(tmp)
中的tmp參數就應給等於你的goroutine個數,同時我們可以使用runtime.NumCPU()
來返回自己電腦中的物理處理器個數,而物理處理器的個數是和自己電腦的處理器掛鉤的
// main方法
func main() {
routine.GOMAXPROCS(2)
fmt.Println(runtime.NumCPU()) // 20
// 實例化計數器wg
var wg sync.WaitGroup
// 要運行兩個goroutine函數
wg.Add(2)
go functions.Demo01(2, "the first goroutine!", &wg)
go functions.Demo01(3, "the second goroutine!", &wg)
// 等待兩個goroutine運行完畢
wg.Wait()
// 執行完畢後再輸出over!
fmt.Println("over!")
}
通過並行執行,我們就可以保證不同的協程是同時運行在不同的邏輯處理器當中,可以實行同時運行