程序,線程,并行和并發
一個應用程式是運作在機器上的一個程序;程序是一個運作在自己記憶體位址空間裡的獨立執行體。一個程序由一個或多個作業系統線程組成,這些線程其實是共享同一個記憶體位址空間的一起工作的執行體。幾乎所有’正式’的程式都是多線程的,以便讓使用者或計算機不必等待,或者能夠同時服務多個請求(如 Web 伺服器),或增加性能和吞吐量(例如,通過對不同的資料集并行執行代碼)。
一個并發程式可以在一個處理器或者核心上使用多個線程來執行任務,但是隻有在同一個程式在某一個時間點在多個些處理核心或處理器上同時執行的任務才是真正的并行。
并行是一種通過使用多處理器以提高速度的能力。是以并發程式可以是并行的,也可以不是。
公認的使用多線程的應用最主要的問題是記憶體中的資料共享,它們會被多線程以無法預知的方式進行操作,導緻一些無法重制或者随機的結果(稱作
競态
)。
在
Go
中,應用程式并發處理的部分被稱作
goroutines
(go協程),它可以進行更有效的并發運算。在協程和作業系統線程之間并無一對一的關系:協程是根據一個或多個線程的可用性,映射(多路複用,執行于)在它們之上的;協程排程器在 Go 運作時很好的完成了這個工作。
Goroutine簡介
Go語言中有個概念叫做goroutine, 這類似我們熟知的線程,但是更加輕量級。
我們先來看一個沒有并發的例子,串行地去執行兩次loop函數:
package main
import "fmt"
func loop() {
for i :=; i <; i++ {
fmt.Printf("%d ", i)
}
}
func main() {
loop()
loop()
}
這裡的輸出是顯而易見的,是
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
。
現在我們把一個loop放在一個goroutine裡跑,我們可以使用關鍵字go來定義并啟動一個goroutine,main()函數變為:
func main() {
go loop()
loop()
}
就加了一個 go 關鍵字 ,輸入就變成了:
0 1 2 3 4 5 6 7 8 9
可是為什麼隻輸出了一趟呢?明明我們主線跑了一趟,也開了一個goroutine來跑一趟啊。
原來,在goroutine還沒來得及跑loop的時候,主函數已經退出了。
如何讓goroutine告訴主線程我執行完畢了?使用一個信道來告訴主線程即可。
無緩沖的信道在取消息和存消息的時候都會挂起目前的goroutine,除非另一端已經準備好。如果不用信道來阻塞主線的話,主線程就會過早跑完,loop線程都沒有機會執行。
本關卡需要強調的是,無緩沖的信道永遠不會存儲資料,隻負責資料的流通。展現在:
- 從無緩沖信道取資料,必須要有資料流進來才可以,否則目前線阻塞
- 資料流入無緩沖信道, 如果沒有其他goroutine來拿走這個資料,那麼目前線阻塞
現在這裡給出代碼,具體Channel的原理和使用在本實訓下一關卡Go語言Channel中詳細講解。
var complete chan int = make(chan int)
func loop() {
for i :=; i <; i++ {
fmt.Printf("%d ", i)
}
complete <- // 執行完畢了,發個消息
}
func main() {
go loop()
<- complete // 直到線程跑完, 取到消息. main在此阻塞住
}