Проверьте, вызывается ли функция как goroutine или нет - PullRequest
1 голос
/ 21 июня 2019

Есть ли способ узнать, была ли запущенная функция вызвана как goroutine или нет?

Я прочитал 'go tour' и мне интересно создать сервер веб-сокетов с golang, поэтому я нашелэтот урок https://tutorialedge.net/golang/go-websocket-tutorial/

Теперь мне интересно, вызывается ли функция wsEndpoint из урока как обычная программа (например, go wsEndpoint (...)) или нет.

Я пыталсяПрочтите документацию по пакету http, но не получите четкую картину, просто предположите, что обработчик будет вызываться с помощью процедуры go.Это правда?

1 Ответ

3 голосов
/ 21 июня 2019

Каждая функция вызывается из goroutine, даже функция main() (которая называется goroutine main).

И у goroutines в Go нет идентификаторов.Неважно, какая программа вызывает функцию.

Чтобы ответить на ваш «оригинальный» вопрос:

Есть ли способ узнать, была ли запущенная функция вызвана как программа или нет?

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

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

По сути, мы можем проверить стек вызовов: список функций, вызывающих друг друга.Если функция находится вверху этого списка, то она вызывается с помощью go (отметьте примечание в конце ответа).Если до этого в стеке вызовов были другие функции, то он был вызван без go из другой функции (которая раньше помещалась в стек вызовов).

Мы можем использовать runtime.Callers(), чтобы получить стек вызывающей программы.Вот как мы можем проверить, есть ли другие функции, вызывающие «нас»:

func f(name string) {
    count := runtime.Callers(3, make([]uintptr, 1))
    if count == 0 {
        fmt.Printf("%q is launched as new\n", name)
    }
}

Тестирование:

func main() {
    f("normal")
    go f("with-go")

    func() { f("from-anon") }()
    func() { go f("from-anon-with-go") }()

    f2("from-f2")
    go f2("with-go-from-f2")

    f3("from-f3")
    go f3("with-go-from-f3")

    time.Sleep(time.Second)
}

func f2(name string) { f(name) }
func f3(name string) { go f(name) }

Это выдаст (попробуйте на Go Playground):

"with-go" is launched as new
"from-anon-with-go" is launched as new
"from-f3" is launched as new
"with-go-from-f3" is launched as new

Примечание: в основном есть функция runtime.goexit() в верхней части всех стеков вызовов, это самая верхняя функция, выполняемая в программе, и этоТочка «выхода» для всех горутин.Вот почему мы пропускаем 3 кадра из стека (0. это само по себе runtime.Callers(), 1. это функция f(), а последний пропускающий это runtime.goexit()).В этой Go Playground вы можете проверить полные стеки вызовов с именами функций и файлов + номерами строк.Это не меняет жизнеспособность этого решения, просто нам нужно пропустить 3 кадра вместо 2, чтобы сказать, был ли вызван f() из другой функции или с помощью оператора go.

...