Go語言之併發程式設計(四)

2022-04-05 05:52:29 字數 3124 閱讀 1700

同步

go 程式可以使用通道進行多個 goroutine 間的資料交換,但這僅僅是資料同步中的一種方法。通道內部的實現依然使用了各種鎖,因此優雅**的代價是效能。在某些輕量級的場合,原子訪問(atomic包)、互斥鎖(sync.mutex)以及等待組(sync.waitgroup)能最大程度滿足需求。

當多執行緒併發執行的程式競爭訪問和修改同一塊資源時,會發生競態問題。

下面的**中有乙個 id 生成器,每次呼叫生成器將會生成乙個不會重複的順序序號,使用 10 個並發生成序號,觀察 10 個併發後的結果。

競態檢測:

package main

import (

"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.go

10

沒有發生競態問題,程式執行正常。

本例中只是對變數進行增減操作,雖然可以使用互斥鎖(sync.mutex)解決競態問題,但是對效能消耗較大。在這種情況下,推薦使用原子操作(atomic)進行變數操作。

互斥鎖(sync.mutex)和讀寫互斥鎖(sync.rwmutex)

互斥鎖是一種常用的控制共享資源訪問的方法,它能夠保證同時只有乙個goroutine可以訪問共享資源。在go程式中的使用非常簡單,參見下面的**:

package main

import (

"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...