同步
go 程式可以使用通道進行多個 goroutine 間的資料交換,但這僅僅是資料同步中的一種方法。通道內部的實現依然使用了各種鎖,因此優雅**的代價是效能。在某些輕量級的場合,原子訪問(atomic包)、互斥鎖(sync.mutex)以及等待組(sync.waitgroup)能最大程度滿足需求。
當多執行緒併發執行的程式競爭訪問和修改同一塊資源時,會發生競態問題。
下面的**中有乙個 id 生成器,每次呼叫生成器將會生成乙個不會重複的順序序號,使用 10 個並發生成序號,觀察 10 個併發後的結果。
競態檢測:
package mainimport (
"fmt"
"sync/atomic"
)var (
// 序列號
seq int64
)// 序列號生成器
func genid() int64
func main()
fmt.println(genid())
}
**說明如下:
在執行程式時,為執行引數加入-race引數,開啟執行時(runtime)對競態問題的分析,命令如下:
# go run -race racedetect.go***************===
warning: data race
write at 0x0000005d3f10 by goroutine 7:
sync/atomic.addint64()
e:/go/src/runtime/race_amd64.s:276 +0xb
main.genid()
d:/go_work/src/chapter09/racedetect/racedetect.go:17 +0x4a
previous read at 0x0000005d3f10 by goroutine 6:
main.genid()
d:/go_work/src/chapter09/racedetect/racedetect.go:18 +0x5a
goroutine 7 (running) created at:
main.main()
d:/go_work/src/chapter09/racedetect/racedetect.go:25 +0x56
goroutine 6 (finished) created at:
main.main()
d:/go_work/src/chapter09/racedetect/racedetect.go:25 +0x56
***************===
10found 1 data race(s)
exit status 66
**執行發生宕機,根據報錯資訊,第18行有競態問題,根據atomic.addint64()的引數宣告,這個函式會將修改後的值以返回值方式傳出:
func genid() int64
再次執行:
# go run -race racedetect.go10
沒有發生競態問題,程式執行正常。
本例中只是對變數進行增減操作,雖然可以使用互斥鎖(sync.mutex)解決競態問題,但是對效能消耗較大。在這種情況下,推薦使用原子操作(atomic)進行變數操作。
互斥鎖(sync.mutex)和讀寫互斥鎖(sync.rwmutex)
互斥鎖是一種常用的控制共享資源訪問的方法,它能夠保證同時只有乙個goroutine可以訪問共享資源。在go程式中的使用非常簡單,參見下面的**:
package mainimport (
"fmt"
"sync"
)var (
// 邏輯中使用的某個變數
count int
// 與變數對應的使用互斥鎖
countguard sync.mutex
)func getcount() int
func setcount(c int)
func main()
**說明如下:
在讀多寫少的環境中,可以優先使用讀寫互斥鎖(sync.rwmutex),它比互斥鎖更加高效。sync包中的rwmutex提供了讀寫互斥鎖的封裝。
我們將互斥鎖例子中的一部分**修改為讀寫互斥鎖,參見下面**:
var (// 邏輯中使用的某個變數
count int
// 與變數對應的使用互斥鎖
countguard sync.rwmutex
)func getcount() int
**說明如下:
等待組(sync.waitgroup)
除了可以使用通道(channel)和互斥鎖進行兩個併發程式間的同步外,還可以使用等待組進行多個任務的同步,等待組可以保證在併發環境中完成指定數量的任務
等待組有下面幾個方法可用,如表1-2所示。
表1-2 等待組的方法
方法名功能
(wg * waitgroup) add(delta int)
等待組的計數器+1
(wg *waitgroup) done()
等待組的計數器-1
(wg *waitgroup) wait()
當等待組計數器不等於0時阻塞直到變0
等待組內部擁有乙個計數器,計數器的值可以通過方法呼叫實現計數器的增加和減少。當我們新增了n個併發任務進行工作時,就將等待組的計數器值增加n。每個任務完成時,這個值減1。同時,在另外乙個goroutine中等待這個等待組的計數器值為0時,表示所有任務已經完成。
// 遍歷這些位址
for _, url := range urls (url)
}// 等待所有的任務完成
wg.wait()
fmt.println("over")
}
**說明如下:
Go語言之併發程式設計(三)
telnet回音伺服器 telnet協議是tcp ip協議族中的一種。它允許使用者 telnet客戶端 通過乙個協商過程與乙個遠端裝置進行通訊。本例將使用一部分telnet協議與伺服器進行通訊。伺服器的網路庫為了完整展示自己的 實現了完整的收發過程,一般比較傾向於使用傳送任意封包返回原資料的邏輯。這...
Go語言之併發程式設計(一)
輕量級執行緒 goroutine 雖然,執行緒池為邏輯編寫者提供了執行緒分配的抽象機制。但是,如果面對隨時隨地可能發生的併發和執行緒處理需求,執行緒池就不是非常直觀和方便了。能否有一種機制 使用者分配足夠多的任務,系統能自動幫助使用者把任務分配到cpu上,讓這些任務盡量併發運作。這種機制在go語言中...
Go語言之Go語言網路程式設計
go語言的 net 包中有乙個 tcpconn 型別,可以用來建立 tcp 客戶端和 tcp 伺服器端間的通訊通道,tcpconn 型別裡有兩個主要的函式 func c tcpconn write b byte n int,err os.error func c tcpconn read b byt...