- 多執行緒程式在一個核的CPU運行

- 多執行緒程式在多個核的CPU運行


goroutine
協程:用戶態,輕量級執行緒,堆疊KB級別,創建和調度由go語言直接調度
執行緒:內核態,執行緒跑多個協程,堆疊MB級別
package main
import (
"fmt"
"time"
)
func hello(i int) {
println("hello goroutine: " + fmt.Sprint(i))
}
func hello_goroutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}

go關鍵字直接可以開啟新的協程
CSP模型

CSP 是 Communicating Sequential Process 的簡稱,中文可以叫做通信順序行程,是一種并發編程模型,是一個很強大的并發資料模型,是上個世紀七十年代提出的,用于描述兩個獨立的并發物體通過共享的通訊 channel(管道)進行通信的并發模型,相對于Actor模型,CSP中channel是第一類物件,它不關注發送訊息的物體,而關注與發送訊息時使用的channel,
Golang 就是借用CSP模型的一些概念為之實作并發進行理論支持,其實從實際上出發,go語言并沒有,完全實作了CSP模型的所有理論,僅僅是借用了 process和channel這兩個概念,process是在go語言上的表現就是 goroutine 是實際并發執行的物體,每個物體之間是通過channel通訊來實作資料共享,
Go語言的CSP模型是由協程Goroutine與通道Channel實作:
- Go協程goroutine: 是一種輕量執行緒,它不是作業系統的執行緒,而是將一個作業系統執行緒分段使用,通過調度器實作協作式調度,是一種綠色執行緒,微執行緒,它與Coroutine協程也有區別,能夠在發現堵塞后啟動新的微執行緒,
- 通道channel: 類似Unix的Pipe,用于協程之間通訊和同步,協程之間雖然解耦,但是它們和Channel有著耦合,
channel
make (chan元素型別,「緩沖模型」)
- 有緩沖通道
make(chan int) - 無緩沖通道
make(chan int,2)

goroutine 和 channel 是 Go 語言并發編程的 兩大基石,Goroutine 用于執行并發任務,channel 用于 goroutine 之間的同步、通信,
Channel 在 gouroutine 間架起了一條管道,在管道里傳輸資料,實作 gouroutine 間的通信;由于它是執行緒安全的,所以用起來非常方便;channel 還提供 “先進先出” 的特性;它還能影響 goroutine 的阻塞和喚醒,
不要通過共享記憶體來通信,而要通過通信來實作記憶體共享,
這就是 Go 的并發哲學,它依賴 CSP 模型,基于 channel 實作,
因為 channel 是一個參考型別,所以在它被初始化之前,它的值是 nil,channel 使用 make 函式進行初始化,可以向它傳遞一個 int 值,代表 channel 緩沖區的大小(容量),構造出來的是一個緩沖型的 channel;不傳或傳 0 的,構造的就是一個非緩沖型的 channel,
兩者有一些差別:非緩沖型 channel 無法緩沖元素,對它的操作一定順序是 “發送 -> 接收 -> 發送 -> 接收 -> ……”,如果想連續向一個非緩沖 chan 發送 2 個元素,并且沒有接收的話,第一次一定會被阻塞;對于緩沖型 channel 的操作,則要 “寬松” 一些,畢竟是帶了 “緩沖” 光環,
對 chan 的發送和接收操作都會在編譯期間轉換成為底層的發送接收函式,
Channel 分為兩種:帶緩沖、不帶緩沖,對不帶緩沖的 channel 進行的操作實際上可以看作 “同步模式”,帶緩沖的則稱為 “異步模式”,
同步模式下,發送方和接收方要同步就緒,只有在兩者都 ready 的情況下,資料才能在兩者間傳輸(后面會看到,實際上就是記憶體拷貝),否則,任意一方先行進行發送或接收操作,都會被掛起,等待另一方的出現才能被喚醒,
異步模式下,在緩沖槽可用的情況下(有剩余容量),發送和接收操作都可以順利進行,否則,操作的一方(如寫入)同樣會被掛起,直到出現相反操作(如接收)才會被喚醒,
package main
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
go func() {
defer close(src)
//Go語言的 defer 陳述句會將其后面跟隨的陳述句進行延遲處理,
//在 defer 歸屬的函式即將回傳時,將延遲處理的陳述句按 defer
//的逆序進行執行,也就是說,先被 defer 的陳述句最后被執行,
//最后被 defer 的陳述句,最先被執行,
for i := 0; i < 10; i++ {
src <- i
}
}()
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
println(i)
}
}
func main() {
CalSquare()
}
chan T // 宣告一個雙向通道
chan<- T // 宣告一個只能用于發送的通道
<-chan T // 宣告一個只能用于接收的通道COPY
并發安全鎖
Eg:對變數執行2000次+1的操作,5個協程并發進行
package main
import (
"fmt"
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
func addWithoutLock() {
for i := 0; i < 2000; i++ {
x += 1
}
}
func add() {
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
fmt.Println("without lock", x)
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
fmt.Println("with lock", x)
}
func main() {
add()
}

waitgroup
WaitGroup是Go語言中的一個型別,它可以用來等待一組Go協程完成,它有三個方法:Add,Done和Wait,Add方法用于將要等待的Go協程數量添加到WaitGroup中;Done方法用于通知WaitGroup一個Go協程已經完成;Wait方法用于阻塞當前Go程,直到WaitGroup內的Go協程都完成,

func manytowait() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
}
1、 Add一個負數
如果計數器的值小于0會直接panic
2、 Add在Wait之后呼叫
比如一些子協程開頭呼叫Add結束呼叫Wait,這些 Wait無法阻塞子協程,正確做法是在開啟子協程之前先Add特定的值,
3、 未置為0就重用
WaitGroup可以完成一次編排任務,計數值降為0后可以繼續被其他任務所用,但是不要在還沒使用完的時候就用于其他任務,這樣由于帶著計數值,很可能出問題,
4、 復制waitgroup
WaitGroup有nocopy欄位,不能被復制,也意味著WaitGroup不能作為函式的引數
本文來自博客園,作者:ivanlee717,轉載請注明原文鏈接:https://www.cnblogs.com/ivanlee717/p/17446482.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553952.html
標籤:其他
上一篇:開始記錄每天的生活
下一篇:返回列表
