Как закрыть зависшую горутину?
У меня есть определенный код, который запускается в горутине, а именно:
func reader(conn *telnet.Conn, nextReadSize int) (string, error) {
nextBytes := make([]byte, nextReadSize)
_, err := conn.Read(nextBytes)
if err != nil {
return "", err
}
return string(nextBytes), nil
}
Он отрабатывает так как положено, но если переменная nextReadSize будет больше чем размер текста готового к считыванию, то горутина ожидает вывод дальше, до тех пор пока nextBytes не будет заполнен весь.
Как сделать так, чтобы горутина закрывалась, если выполняется больше, скажем, 1 секунды?
Можно конечно, через select ожидать вывода в канал и каждый раз открывать новую горутину, но не слишком ли это расточительно? Я понимаю, когда одна две горутины, а когда их несколько тысяч?
Также есть вариант считывать по одному байту, но считывать лог размером в несколько тысяч строк по байту, так себе идея.
Спасибо
Ответы (2 шт):
Используйте методы типа net.Conn для установки таймаутов. Таких методов три:
- Общий таймаут
- Таймаут на чтение
- Таймаут на запись
В данном случае -- вам интересна установка таймаута на чтение.
ВНИМАНИЕ! Таймаут устанавливается в форме конкретного момента времени, поэтому его НАДО ВЫСТАВЛЯТЬ КАЖДЫЙ РАЗ перед КАЖДЫМ ЧТЕНИЕМ.
Работу одной гороутину в принципе нельзя принудительно остановить из другой. Если нужен механизм управления процессом завершения работы, то его нужно реализовать отдельно. Можно использовать таймауты, контекст, передачу команды на завершения каналом или что-то еще, много вариантов. пример
Старайтесь не делать методов, которые могут работать вечно. Для длительных процессов больше подходит бесконечный цикл с выполнением задачи маленькими порциями и регулярной проверкой условия завершения работы (например отмена контекста).
var pigsFly = false
// процесс с неопределенным временем работы
func longProcess(ctx context.Context, params ...interface{}) {
// канал по которому придет отмена контекста
killed := ctx.Done()
Loop1:
for {
// проверка отмены контекста
select {
case <-killed:
break Loop1 // завершение работы, если контекст отменен
default:
}
// процесс с определенным временем работы
time.Sleep(time.Second)
fmt.Printf("Im alive %v\n", time.Now())
// проверка выполнения задачи
if pigsFly {
fmt.Printf("Pigs fly! %v\n", time.Now())
break Loop1
}
}
}
При таком подходе вам незачем беспокоиться, полетят ли когда-то свиньи. Вы можете передать контекст с таймаутом, и процесс завершиться после таймаута, или отменить контекст вручную в любое удобное время в другой гороутине.