[英]Thread within struct, function arguments too large for new goroutine
我創建了這個簡單的應用程序來演示我遇到的問題。
package main
import (
"fmt"
"unsafe"
"sync"
)
type loc_t struct {
count [9999]int64
Counter int64
}
func (l loc_t) rampUp (wg *sync.WaitGroup) {
defer wg.Done()
l.Counter += 1
}
func main() {
wg := new(sync.WaitGroup)
loc := loc_t{}
fmt.Println(unsafe.Sizeof(loc))
wg.Add(1)
go loc.rampUp(wg)
wg.Wait()
fmt.Println(loc.Counter)
}
如果我執行上述操作,我將收到一個fatal error: newproc: function arguments too large for new goroutine
runtime stack: runtime: unexpected return pc for runtime.systemstack called from 0x0
fatal error: newproc: function arguments too large for new goroutine
runtime stack: runtime: unexpected return pc for runtime.systemstack called from 0x0
現在的原因是使用go
生成后台任務時堆棧大小為2k。 有趣的是,我只傳遞了一個稱為被調用函數的指針。 這個問題在我的生產環境中發生了,顯然是不同的結構,所有的東西都工作了一年,然后突然間它開始拋出這個錯誤。
像其他任何參數一樣,方法接收者將傳遞給方法調用。 因此,如果該方法具有非指針接收器,則將復制您所用的整個結構。 如果可以的話,最簡單的解決方案是使用指針接收器。
如果必須使用非指針接收器,則可以通過不將方法調用作為goroutine而是其他函數(可能是函數文字)啟動來規避此問題:
go func() {
loc.rampUp(wg)
}()
如果可以同時修改loc
變量(在調度啟動的goroutine並將其復制為rampUp()
方法之前),則可以手動創建它的副本並在goroutine中使用它,如下所示:
loc2 := loc
wg.Add(1)
go func() {
loc2.rampUp(wg)
}()
這些解決方案之所以有效,是因為啟動新的goroutine不需要大的初始堆棧,因此初始堆棧限制不會受到影響。 並且堆棧大小是動態的,因此啟動后它將根據需要增長。 可以在這里閱讀詳細信息: Go是否具有等效的“無限調用堆棧”?
堆棧大小的問題顯然是結構本身的大小。 因此,隨着您的結構自然增長,您可能會像我一樣越過2k堆棧調用大小。
可以通過在函數聲明中使用指向該結構的指針來解決上述問題。
func (l *loc_t) rampUp (wg *sync.WaitGroup) {
defer wg.Done()
l.Counter += 1
}
這將創建一個指向該結構的指針,因此進入堆棧的只是該指針,而不是該結構的整個副本。
顯然,如果您要同時在多個線程中進行調用,這可能會帶來其他影響,包括競爭條件。 但是,作為不斷增長的結構(突然開始引起堆棧溢出)的解決方案,這是一個解決方案。
無論如何,希望這對外面的人有幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.