2 回答

TA貢獻(xiàn)1821條經(jīng)驗(yàn) 獲得超5個(gè)贊
您有多個(gè)可以同時(shí)運(yùn)行的工作人員,并且所有工作人員都可以同時(shí)嘗試和發(fā)出請(qǐng)求。由于requestChan
是無(wú)緩沖的,它們都阻塞等待讀取器同步并接受他們的請(qǐng)求。
您有多個(gè)生成器,它們將通過(guò) 與請(qǐng)求者同步requestChan
,生成結(jié)果,然后阻塞無(wú)緩沖,generatorChan
直到工作人員讀取結(jié)果。請(qǐng)注意,它可能是不同的工人。
沒(méi)有額外的同步,所以其他一切都是不確定的。
一個(gè)生成器可以處理所有請(qǐng)求。
生成器可以獲取請(qǐng)求并
seq
在任何其他生成器有機(jī)會(huì)運(yùn)行之前通過(guò)遞增。只有一個(gè)處理器,這甚至是可能的。所有的生成器都可以獲取請(qǐng)求,并且最終都想要
seq
在完全相同的時(shí)間遞增,從而導(dǎo)致各種問(wèn)題。工作人員可以從他們碰巧發(fā)送到或來(lái)自完全不同的生成器的同一生成器中獲得響應(yīng)。
通常,如果不添加同步來(lái)強(qiáng)制執(zhí)行這些行為中的一種,則無(wú)法確保其中任何一種行為確實(shí)發(fā)生。
請(qǐng)注意,對(duì)于數(shù)據(jù)競(jìng)爭(zhēng),這本身就是另一個(gè)非確定性事件。有可能獲得任意值、程序崩潰等。假設(shè)在競(jìng)爭(zhēng)條件下該值可能只是被一個(gè)或一些這樣的相對(duì)無(wú)害的結(jié)果關(guān)閉是不安全的。
對(duì)于試驗(yàn),您能做的最好的事情就是加速GOMAXPROCS
。通過(guò)環(huán)境變量(例如類(lèi)似env GOMAXPROCS=16 go run foo.go
或env GOMAXPROCS=16 ./foo
之后go build
)或runtime.GOMAXPROCS(16)
從您的程序調(diào)用。默認(rèn)值為 1,這意味著可能會(huì)隱藏?cái)?shù)據(jù)競(jìng)爭(zhēng)或其他“奇怪”行為。
您還可以通過(guò)在不同的點(diǎn)添加調(diào)用runtime.Gosched
或time.Sleep
在不同的點(diǎn)上來(lái)影響一些事情。
如果您使用競(jìng)爭(zhēng)檢測(cè)器(例如使用go run -race foo.goo
或go build -race
),您還可以看到數(shù)據(jù)競(jìng)爭(zhēng)。程序不僅應(yīng)該在退出時(shí)顯示“Found 1 data race(s)”,而且還應(yīng)該在首次檢測(cè)到競(jìng)爭(zhēng)時(shí)轉(zhuǎn)儲(chǔ)大量帶有堆棧跟蹤的詳細(xì)信息。
這是用于實(shí)驗(yàn)的代碼的“清理”版本:
package main
import (
"log"
"sync"
"sync/atomic"
)
var seq uint64 = 0
var generatorChan = make(chan uint64)
var requestChan = make(chan uint64)
func generator(genID int) {
for reqID := range requestChan {
// If you want to see a data race:
//seq = seq + 1
// Else:
s := atomic.AddUint64(&seq, 1)
log.Printf("Gen: %2d, from %3d", genID, reqID)
generatorChan <- s
}
}
func worker(id int, work *sync.WaitGroup) {
defer work.Done()
for i := 0; i < 5; i++ {
requestChan <- uint64(id)
log.Printf("\t\t\tWorker: %3d got %4d", id, <-generatorChan)
}
}
func main() {
log.SetFlags(log.Lmicroseconds)
const (
numGen = 20
numWorker = 200
)
var wg sync.WaitGroup
for i := 0; i < numGen; i++ {
go generator(i)
}
wg.Add(numWorker)
for i := 0; i < numWorker; i++ {
go worker(i, &wg)
}
wg.Wait()
close(requestChan)
}
Playground(但請(qǐng)注意,playground 上的時(shí)間戳沒(méi)有用,調(diào)用runtime.MAXPROCS可能沒(méi)有任何作用)。進(jìn)一步注意,playground 會(huì)緩存結(jié)果,因此重新運(yùn)行完全相同的程序?qū)⑹冀K顯示相同的輸出,您需要進(jìn)行一些小的更改或僅在您自己的機(jī)器上運(yùn)行它。
很多小的變化,比如關(guān)閉生成器,使用logvsfmt因?yàn)榍罢呖梢员WC并發(fā)性,消除數(shù)據(jù)競(jìng)爭(zhēng),使輸出看起來(lái)更好等。

TA貢獻(xiàn)1820條經(jīng)驗(yàn) 獲得超10個(gè)贊
渠道類(lèi)型
通道為并發(fā)執(zhí)行函數(shù)提供了一種機(jī)制,通過(guò)發(fā)送和接收指定元素類(lèi)型的值來(lái)進(jìn)行通信。未初始化通道的值為 nil。
可以使用內(nèi)置函數(shù) make 創(chuàng)建一個(gè)新的初始化通道值,該函數(shù)將通道類(lèi)型和可選容量作為參數(shù):
make(chan?int,?100)容量(以元素?cái)?shù)為單位)設(shè)置通道中緩沖區(qū)的大小。如果容量為零或不存在,則通道沒(méi)有緩沖,只有當(dāng)發(fā)送方和接收方都準(zhǔn)備好時(shí),通信才能成功。否則,如果緩沖區(qū)未滿(發(fā)送)或非空(接收),則通道被緩沖并且通信成功而不會(huì)阻塞。一個(gè) nil 通道永遠(yuǎn)不會(huì)準(zhǔn)備好進(jìn)行通信。
您正在使用無(wú)緩沖通道來(lái)限制通道通信。
例如,
generatorChan?=?make(chan?uint64) requestChan?=?make(chan?uint64)
- 2 回答
- 0 關(guān)注
- 248 瀏覽
添加回答
舉報(bào)