Поток в структуре, аргументы функции слишком велики для новой программы - PullRequest
0 голосов
/ 12 июня 2018

Я создал это простое приложение, чтобы продемонстрировать проблему, с которой я столкнулся.

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

Теперь причина дляэто размер стека 2 КБ, когда go используется для запуска фоновой задачи.Что интересно, я только передаю указатель на вызываемую функцию.Эта проблема произошла со мной на производстве, очевидно, с другой структурой, все работало в течение года, а затем внезапно начала выдавать эту ошибку.

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

Получатели метода передаются вызовам метода, как и любой другой параметр.Поэтому, если у метода есть получатель без указателя, вся структура в вашем случае будет скопирована.Самое простое решение - использовать получатель указателя, если вы можете.

Если вы должны использовать получатель без указателя, то вы можете обойти это, не запуская вызов метода как процедуру, а другую функцию, возможно,литерал функции:

go func() {
    loc.rampUp(wg)
}()

Если переменная loc может быть изменена одновременно (до того, как запущенная программа запишется в расписание и скопирует ее для метода rampUp()), вы можете создать ее копию вручнуюи используйте это в goroutine, например так:

loc2 := loc
wg.Add(1)
go func() {
    loc2.rampUp(wg)
}()

Эти решения работают, потому что запуск новой goroutine не требует большого начального стека, поэтому начальный предел стека не будет мешать.И размер стека является динамическим, поэтому после запуска он будет расти по мере необходимости.Подробности можно прочитать здесь: Есть ли в Go эквивалент "бесконечного стека вызовов"?

0 голосов
/ 12 июня 2018

Проблема с размером стека, очевидно, связана с размером самой структуры.Так как ваша структура растет органически, вы, как и я, можете пересечь этот размер вызова стека 2 КБ.

Приведенную выше проблему можно исправить, используя указатель на структуру в объявлении функции.

func (l *loc_t) rampUp (wg *sync.WaitGroup) {
    defer wg.Done()
    l.Counter += 1
}

Это создает указатель на структуру, так что все, что идет в стек, это указатель, а не полная копия структуры.

Очевидно, что это может иметь и другие последствия, включая условия гонки, если вы 'повторный вызов в нескольких потоках одновременно.Но как решение для постоянно растущей структуры, которая внезапно начнет вызывать переполнение стека, это решение.

В любом случае, надеюсь, что это будет полезно для кого-то еще.

...