我嘗試在我的程式中使用比賽標志并發現問題:(
函式如下
func (g *WaitingErrorGroup) Start(run func() (bool, error)) {
g.g.Start(func() {
requeue, err := run()
if g.err == nil {
g.err = err
}
if requeue {
g.requeue = requeue
}
})
}
該函式的呼叫如下
g.Start(func() (bool, error) {
return install(vins, crObjectKey, releasePrefix, kFilePath, objectCli, dependenciesBroadcastingSchema, compStatus)
})
g.Start(func() (bool, error) {
return false, uninstall(currentRelease, kFilePath, updateChartStatus)
})
堆疊跟蹤如下所示
WARNING: DATA RACE
Read at 0x00c0001614a8 by goroutine 82:
github.vs.sar/agm/coperator/components/tools.(*WaitingErrorGroup).Start.func1()
/Users/github.vs.sar/agm/coperator/components/tools/waitgroup.go:27 0x84
k8s.io/apimachinery/pkg/util/wait.(*Group).Start.func1()
/Users/i88893/go/pkg/mod/k8s.io/apimachinery@v0.22.4/pkg/util/wait/wait.go:73 0x6d
開始功能是這樣的:( 我的代碼) github.vs.sar/agm/coperator/components/tools.(*WaitingErrorGroup).Start.func1()
func (g *WaitingErrorGroup) Start(run func() (bool, error)) {
g.g.Start(func() {
requeue, err := run()
if g.err == nil {
g.err = err
}
if requeue {
g.requeue = requeue
}
})
}
堆疊跟蹤中的第二個是這個(不是我的代碼)
go/pkg/mod/k8s.io/[email protected]/pkg/util/wait/wait.go:73 0x6d
// Start 在組中的一個新 goroutine 中啟動 f。
func (g *Group) Start(f func()) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
f()
}()
}
我猜(不是圍棋專家)這與g.errfrom multiplegoroutines同時使用有關,這是不允許的。寫作也一樣g.requeue
知道如何解決這個問題嗎?
也許我需要使用https://pkg.go.dev/sync#RWMutex 但不知道如何......
更新
I took @Danil suggestion (change the lock position) and change it like following added mutex in the struct and add lock in the function, does it make sense ? Now when I run with race flag everything seems to be OK
type WaitingErrorGroup struct {
g *wait.Group
mu sync.Mutex
err error
requeue bool
}
func (g *WaitingErrorGroup) Start(run func() (bool, error)) {
g.g.Start(func() {
g.mu.Lock()
defer g.mu.Unlock()
requeue, err := run()
if g.err == nil {
g.err = err
}
if requeue {
g.requeue = requeue
}
})
}
uj5u.com熱心網友回復:
您可以使用通道來傳達發生的錯誤并處理它們。
例如,像這樣的東西。
func handleErrors(c chan error) *sync.WaitGroup {
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
for err := range c {
fmt.Println(err)
}
}()
return &wg
}
func main() {
c := make(chan error, 2)
wg := sync.WaitGroup{}
defer handleErrors(c).Wait()
defer close(c)
defer wg.Wait()
wg.Add(2)
go func() {
defer wg.Done()
c <- errors.New("error 1")
}()
go func() {
defer wg.Done()
c <- errors.New("error 2")
}()
}
我認為使用通道比其他同步原語(如鎖)更慣用。鎖更難正確處理,而且會帶來性能成本。
如果一個 goroutine 有鎖,其他 goroutine 必須等到鎖被釋放。因此,您在并發執行中引入了瓶頸。在上面的例子中,這是通過緩沖通道來解決的。即使還沒有任何人讀取訊息,兩個 goroutine 仍然能夠傳遞他們的訊息而不會被阻塞。
此外,在使用鎖時,可能會出現鎖永遠不會被釋放的情況,例如,如果程式員忘記添加相關行,從而導致死鎖情況。盡管在通道未關閉時可能會發生類似的壞事。
uj5u.com熱心網友回復:
出現問題是因為您嘗試g.err從不同的 goroutine 操作不同步的共享記憶體(在您的情況下)。
要解決此錯誤,您需要同步對g.err.
您可以使用sync.Mutex和sync.RWMutex
在您的情況下,您將擁有:
func (g *WaitingErrorGroup) Start(run func() (bool, error)) {
g.g.Start(func() {
requeue, err := run()
// Lock before reading and writing g.err and unlock after
g.mu.Lock()
defer g.mu.Unlock()
if g.err == nil {
g.err = err
}
if requeue {
g.requeue = requeue
}
})
}
根據在此處使用sync.WaitGroup的建議- 不確定這是否適合您。sync.WaitGroup 用于同步 goroutine 的集合。
WaitGroup 等待一組 goroutine 完成。主 goroutine 呼叫 Add 來設定要等待的 goroutine 的數量。然后每個 goroutine 運行并在完成時呼叫 Done。同時,Wait 可以用來阻塞,直到所有的 goroutine 都完成。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/414942.html
標籤:
