#go #channel #panic
#Вперед #канал #паника
Вопрос:
Есть код:
func (c *Connector) SendPacketFuture(p []byte) (future chan []byte) {
defer func() {
// TODO Check r to catch only chan panics
if r := recover(); r != nil {
future = nil
}
}()
t := newConnectorTask(p)
c.tasks <- t
future = t.PacketFromServerChan
return
}
TODO
это довольно понятно.
c.tasks
это канал, и он может быть закрыт другой подпрограммой. Поскольку нет безопасного способа отправки на канал, который можно закрыть, я ловлю панику здесь. Проблема в том, что могут возникать разные паники, и я хочу отреагировать на ту, которая возникает, путем записи в закрытый канал.
Возможно ли это сделать в Go?
Комментарии:
1. В общем, вы не должны пытаться это обнаружить, и есть другой способ создания программы. Есть причина, по которой это паника, предназначенная для сбоя программы.
2. Он построен вокруг TTL-кэша. Существует редкое состояние гонки, когда мы пытаемся создать future на соединителе, срок действия которого только что истек и был удален из кэша. Я могу добавить SharedMutex для синхронизации вещей, но это только увеличит постоянную стоимость времени выполнения при решении потенциальной проблемы RC. Борьба с паникой кажется мне разумным взломом
3. Но почему канал закрывается? Закрытие — это сигнал, который отправляет отправитель (обычно), отсюда и паника при закрытии канала.
4. Закрывая, я уведомляю отправителей о том, что ссылка, которую они хранят, больше не актуальна. У меня есть этот код в конце основной функции соединителя:
close(c.tasks); for t := range c.tasks { close(t.PacketFromServerChan); }
5. Обычно вы выбираете отдельный канал для подачи сигнала о выходе: play.golang.org/p/-2D4P70zm8
Ответ №1:
Сообщение о восстановлении в этом случае представляет собой не сообщенное значение ошибки из среды выполнения со строковым значением "send on closed channel"
.
Единственное, что вы можете сделать здесь, это сопоставить строку ошибки:
if e, ok := r.(error); ok amp;amp; w.Error() == "send on closed channel" {
fmt.Println("recover from send on closed channel")
}
https://play.golang.org/p/LNcfdE9Bg2
На самом деле, вам, вероятно, нужен отдельный канал для подачи сигнала о выходе.
t := newConnectorTask(p)
select {
case <-c.close:
// closing the c.close channel will unblock this case
return
case c.tasks <- t:
}
Комментарии:
1. @ViacheslavKroilov: Я должен упомянуть, что, хотя это маловероятно, что это изменится, нет никакой гарантии, что это продолжит работать в будущих версиях Go или что это работает с другими реализациями Go, поскольку значение ошибки не указано.
2. Что, если я смоделирую эту панику, а затем сохраню тип ошибки? Это должно быть то же самое во время выполнения, не так ли?
3. @ViacheslavKroilov: должно быть, но технически все еще нет ничего, что мешало бы среде выполнения добавлять динамическую информацию в эту строку. Если вы собираетесь это сделать, я бы просто определил постоянную строку и исправил ее, если она сломается.
4. @ViacheslavKroilov: тип ошибки не является специфичным для этой паники, в настоящее время она вызывается
plainError