簡體   English   中英

結構中的線程,函數參數對於新的goroutine太大

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM