Comments 31
Вопрос знатокам: как прервать выполнение go routine с io.Copy внутри? Сразу скажу, что блокировка происходит на этой строке https://golang.org/src/io/io.go#L380. Интерфейс Close тоже не помогает.
Пока мне никто не смог ответить на этот вопрос.
Если ваш кейс — чтение файлов и ОС Linux, то может помочь вот этот пакет — https://github.com/npat-efault/poller
В моем случае это stdin и stdout.
И как этот тред завершит io.Copy, если src.Read(buf)
заблокирован?
go routine это же легковесный тред. тред процесса нельзя завершить не завершив сам процесс. если же ты говоришь о сигнале, который передаётся через канал, то Read(buf)
блокирует всю go routine функцию, и нет возможности обработать сигнал, полученный с канала.
Может я тебя не так понял, но вот пример. Висит, пока его не прибьёшь KILL'ом.
https://play.golang.org/p/s2_qrgnbPQ
Потом как ты себе представляешь production приложение, которое само себе сигналы шлет? Тесты уж куда ни шло.
По мне это выглядит как самому себе почту отправлять. Можно, но зачем?
Если интерес всё еще не пропал, то ниже выложил пример, который необходимо заставить правильно работать. https://habrahabr.ru/post/306914/#comment_9733560
И жутко асинхронный. Ну пошлёт горутина сигнал, а ну как он прилетит когда read() уже отработал и Go уже начал обработку совершенно другой горутины? Ведь это вполне возможно, между принятием решения и, собственно, посылкой сигнала проходит время. Прилетает он тоже не мгновенно.
Полагаю, обработчик в этом случае «просто ничего не делай»: read и так вернётся с EINTR, а влияния на код никакого не будет… если только «совершенно другая горутина» не начала свой read, который вы немедленно прибьёте задолго до timeout’а (если он вообще будет нужен).
И ещё, откуда вы собрались добывать актуальный PID нужной нити? Go может взять и переместить горутину с одной системной нити на другую, никаких гарантий тут нет.
В общем, это попытка подковать блоху кузнечным молотом. Если руки алмазные, то пройдёт, но, скорее, добавите себе пачку ошибок.
В общем можно, но сломаться это может в любой момент, когда чего-нить в планировщике поменяют.
Проще скинуть ссылку на github issue: https://github.com/coreos/fleet/issues/1499#issuecomment-209445880
Таймауты не вариант, они решают только последствия частных случаев. Даже если выделить goroutine на stdin — stdin останется открытым и следующая go routine не сможет с ним нормально работать, пока пользователь не нажмет enter.
я пробовал закрыть файловый дескриптор на stdin, тогда goroutine завершается. но и терминал перестаёт работать.
в смысле последующая?
func input(dc chan []byte) {
defer close(dc)
for {
data := make([]byte, 128)
n, err := os.Stdin.Read(data)
if n > 0 {
dc <- data[0:n]
}
if err != nil {
break
}
}
}
вот такая горутина будет читать из stdin и писать в канал а дальше уже где нужно делаете worker, process или что у вас по смыслу где будете читать из этого канала:
func worker(dc chan []byte, done chan bool) {
select {
case data, ok := <- dc:
if !ok {
// chanel closed exit
return
}
// do some work
case <- done:
// exit
return
}
}
соответственно когда нужно завершить эту горутину пишите в канал done
Эту, это какую? worker? А какой смысл мне её завершать? Мне в данном случае требуется завершить input
и прервать os.Stdin.Read()
. Чтобы в следующем цикле я бы смог использовать os.Stdin
для других целей.
@neolink меня поправит, если что, но думаю, имелось в виду другое — вы создаёте себе одну горутину, которая у вас будет создаваться сразу на старте, жить до самого завершения вашей программы, и задачей у неё будет читать os.Stdin и писать всё прочитанное в канал. (Или pipe, например, можно попробовать.) Тогда этот канал становится вашим stdin, вы можете завершать одних его слушателей и назначать других по необходимости. А горутина, которая читает os.Stdin, завершится сама на выходе приложения.
Вот пример кода, который необходимо заставить правильно работать:
https://gist.github.com/kayrus/2753b4710e78dd0f5e544baa0f5f4fa1
Нужно завершить go routine (и вернуть stdin в исходное состояние) три раза так, чтобы результирующий вывод консоли при вводе 123
был:
Scanning:
123
What was scanned at i=0: "123"
Scanning:
123
What was scanned at i=1: "123"
Scanning:
123
What was scanned at i=2: "123"
а то у вас прям сразу рейс между основным кодом и той горутиной что вы запускаете, как бы не очень понятно какой результат вы ожидаете
Это файл, скорее всего. Думаю, kay вот на эту проблему налетел: Issue 10001. Тогда Close() может не помочь, POSIX не обещает, что read(2) и close(2) не вызовут гонок, если вызвать из разных потоков. Поллер по ссылке выше, наверное, самое идиоматичное решение — жаль, что из коробки не идёт.
Спасибо автору за перевод, но все же почему нигде нет ссылки на оригинальную статью? Если она все же есть, просьба ткнуть носом)
Разбираемся в Go: пакет io