quarkus是如何支持虛擬線程的呢?今天咱們一起來閱讀quarkus源碼,學習從框架開發視角去添加新特性,除了開闊眼界,也為為自己的設計能力提升增加有效的參考信息 ...
已經把《Go 語言併發之道》通讀了一遍,非常不錯的一本書,對於理解掌握Go語言的併發知識有很大的幫助,接下來我會把書中有用的知識通過代碼示例出來,把一些比較好的知識點記錄下來。
首先我們來看一段代碼
var data int
go func() { data++ }()
if data == 0 {
fmt.Println(" the value is 0.")
} else {
fmt.Printf(" the value is %v.\n", data)
}
這段代碼我們想把data + 1 後列印出來,但是結果列印出來的效果是 “the value is 0.”, 沒有達到我們的預期,主要是因為調用 data++ 的這個goroutine沒有先執行。 併發的難點就是控製程序執行的先後順序。 那麼我們能不能這樣改進一下呢? 加一個sleep讓主goroutine等待一下。
var data int
go func() { data++ }()
time.Sleep(1 * time.Second)
if data == 0 {
fmt.Println(" the value is 0.")
} else {
fmt.Printf(" the value is %v.\n", data)
}
列印出來的結果是 “the value is 1.”, 似乎達到了我們的預期, 但是實際是是不行的,也許data++方法裡面還有耗時的操作,那程式運行的結果又有不確定性了。
這個程式還有個很大的問題, 兩個goroutine共用了記憶體data, 如果這個data是只讀的那還好,不會有什麼影響,但是如果它是需要改變的,那麼兩個goroutine拿到的數據會存在不確定性。那麼是否能夠通過加鎖來解決這個問題呢? 如下示例代碼
var memoryAccess sync.Mutex
var data int
go func() {
memoryAccess.Lock()
data++
memoryAccess.Unlock()
}()
memoryAccess.Lock()
if data == 0 {
fmt.Println(" the value is 0.")
} else {
fmt.Printf(" the value is %v.\n", data)
}
memoryAccess.Unlock()
通過加鎖我們能保證訪問data的時候是獨占的,但是它依然解決不了代碼執行先後問題,而且看上去很不優雅。 既優雅又能解決問題的方法是下麵的代碼
c := make(chan int)
var data int
go func() {
data++
c <- data
}()
data = <-c
if data == 0 {
fmt.Println(" the value is 0.")
} else {
fmt.Printf(" the value is %v.\n", data)
}
輸出結果“the value is 1.”
上面的代碼定義了一個channel, 通過channel來傳遞value, 主goroutine遇到讀取channel( <-c)的地方會等待,等待副goroutine把值填進去(c <- data), 其它部分的代碼併發執行。
這裡貼出書上Go語言併發性哲學總結: 追求簡潔, 儘量使用channel, 並且認為goroutine的使用是沒有成本的。
Go語言的座右銘: 使用通信來共用記憶體,而不是通過共用記憶體來通信
總結
這個例子雖然很簡單,但是它能夠引導我們進入Go語言併發的世界,讓我們明白如何通過goroutine和channel來優雅的寫出併發代碼。 後續我們將列舉出更多的示例,通過示例來說明一下知識。