Проблема с вашим кодом двоякая.
Во-первых, теоретически - утечка маршрутной программы, поскольку любая попытка отправить значение в канал с нулевой пропускной способностью (небуферизованный канал или заполненный буферизованный канал) блокирует отправляющую программу до получения операция выполняется на этом канале.
Итак, да, по определению того, как работают каналы, все ваши три процедуры будут заблокированы в операторе numChan <- num
.
Во-вторых, начиная с некоторой ревизии Go, необработанный panic
по умолчанию сбрасывает только след стека из программы паники.
Если вы хотите сбросить стеки всех активных подпрограмм, вам нужно настроить время выполнения - из документации пакета runtime
:
Переменная GOTRACEBACK
управляет объемом выходных данных, генерируемых в случае сбоя программы Go из-за невосстановленной паники или неожиданного состояния во время выполнения. По умолчанию ошибка печатает трассировку стека для текущей программы, исключая внутренние функции системы выполнения, а затем завершает работу с кодом выхода 2. Ошибка печатает трассировки стека для всех программ, если текущая программа не работает или происходит сбой. внутренний для времени выполнения. GOTRACEBACK=none
полностью исключает следы стека goroutine. GOTRACEBACK=single
(по умолчанию) ведет себя так, как описано выше. GOTRACEBACK=all
добавляет трассировки стека для всех пользовательских процедур. GOTRACEBACK=system
похож на «все», но добавляет кадры стека для функций времени выполнения и отображает процедуры, созданные внутри во время выполнения. GOTRACEBACK=crash
походит на «систему», но вылетает в зависимости от операционной системы вместо выхода. Например, в системах Unix сбой вызывает SIGABRT
, чтобы вызвать дамп ядра. По историческим причинам настройки GOTRACEBACK
0, 1 и 2 являются синонимами для none, all и system соответственно. Функция The runtime/debug
пакета SetTraceback
позволяет увеличить объем выходных данных во время выполнения, но не может уменьшить объем ниже значения, указанного в переменной среды. См https://golang.org/pkg/runtime/debug/#SetTraceback.
Также обратите внимание, что вы не должны когда-либо использовать таймеры для (имитации) синхронизации: в примере с игрушкой это может сработать, но в реальной жизни ничто не препятствует тому, чтобы у ваших трех подпрограмм не было шанс на запуск в течение промежутка времени, в течение которого ваша основная программа была потрачена на вызов time.Sleep
, поэтому в результате может быть запущено любое количество порожденных программ: от 0 до 3.
Добавьте сюда тот факт, что когда main
выходит из среды выполнения, он просто убивает все выдающиеся активные программы, и результат теста может быть в лучшем случае неожиданным.
Следовательно, правильным решением было бы
- Просто напечатайте стеки там, где нужно,
- Удостоверьтесь, что вы синхронизируете посылки с совпадающими получениями:
package main
import (
"fmt"
"log"
"runtime"
)
func dumpStacks() {
buf := make([]byte, 32 * 1024)
n := runtime.Stack(buf, true)
log.Println(string(buf[:n]))
}
func main() {
numCount := 3
numChan := make(chan int, numCount)
for i := 0; i < numCount; i++ {
go func(num int) {
fmt.Printf("Adding num: %d to chan\n", num)
numChan <- num
fmt.Printf("Adding num: %d to chan Done\n", num)
}(i)
}
dumpStacks()
for i := 0; i < numCount; i++ {
<-numChan
}
}
Детская площадка .