Каждая функция вызывается из 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
.