Могу ли я принудительно завершить горутину, не дожидаясь ее возврата?

Позвольте мне использовать пример:

func WaitForStringOrTimeout() (string, error) {
  my_channel := make(chan string)
  go WaitForString(my_channel)

  select {
  case found_string := <-my_channel:
    return found_string, nil
  case  <-time.After(15 * time.Minute):
    return nil, errors.New("Timed out waiting for string")
  }
}

В этом простом примере у меня есть некоторая функция WaitForString, которая блокируется на некоторое время и в конечном итоге может вернуть строку. Я хочу обернуть WaitForString этим кодом, который либо возвращает ту же строку, либо истекает с ошибкой.

Если строка найдена быстро, где-то все еще работает горутина с 15-минутным оператором сна или этот мусор как-то собирается?

Если время ожидания истекло, а строка так и не была найдена, существует ли горутина, выполняющая WaitForString, даже если нет других подпрограмм, которые могли бы наблюдать за ее выводом? Что, если WaitForString выделяет много памяти, но никогда не возвращается?

Есть ли способ заставить WaitForString() узнать о тайм-ауте и сдаться?


person Gregable    schedule 18.02.2014    source источник
comment
Да, он будет продолжать работать, даже если выходной канал закрыт. Обнаружение тайм-аута должно жить в WaitForString, я не думаю, что есть способ очистить горутину, обернув ее.   -  person az_    schedule 18.02.2014
comment
Конечно, я мог бы изменить функцию WaitForString(), но это простой пример. Что, если второй случай не является тайм-аутом, а вместо этого является WaitForOtherString(). Мне нужно только подождать, пока один из них не закончит. Затем я снова застрял, так как мне все еще нужна функция над обеими этими двумя с оператором select. Как я могу остановить более медленную горутину?   -  person Gregable    schedule 18.02.2014
comment
В конечном счете, вам нужно управлять прерываниями самостоятельно. Взгляните, например, на play.golang.org/p/bpOGkIN9Ng. Функция WaitForString() должна знать, что она может быть прервана, и должен быть механизм для ее прерывания. Возможно, вы также можете использовать панику/восстановление.   -  person az_    schedule 18.02.2014
comment
Это то, о чем я беспокоился. Таким образом, если бы WaitForString приняла выходной канал, но затем вызвала другие функции, которые могли бы заблокироваться, мне нужно было бы создать еще один канал для каждой из этих функций, вызвать их внутри выбора со всеми средствами, которые вы показали в своей демонстрации, и затем скажите им выйти пораньше.   -  person Gregable    schedule 18.02.2014


Ответы (1)


В общем, нет способа остановить другую горутину. Существует функция runtime.Goexit, которую можно использовать для выхода из текущей горутины (даже если она вызвана из кадра глубокого вызова), но ничего, что могло бы вызвать выход из других горутин.

Для конкретного случая модуля time нет отдельной горутины, обрабатывающей каждый таймер или тикер: вместо этого среда выполнения централизованно управляет таймерами, поэтому она может определить, когда ей в следующий раз нужно проснуться.

Пока горутина не висит рядом, канал и небольшая бухгалтерская структура останутся на 15 минут.

Если это проблема, рассмотрите возможность использования time.NewTimer вместо time.After и вручную остановите таймер, когда вернетесь. Например:

t := time.NewTimer(15 * time.Minute)
defer t.Stop()
select {
case found_string := <-my_channel:
    return found_string, nil
case  <-t.C:
    return nil, errors.New("Timed out waiting for string")
}

time.After действительно полезен для точного периодического поведения, тогда как time.NewTimer отлично подходит для простых тайм-аутов.

person James Henstridge    schedule 18.02.2014
comment
Связано, значит ли это, что модуль времени - это то, что я не мог реализовать в go? IE: Могу ли я написать горутину, которая работает так же, как таймер, и активируется чем-то внешним по отношению к коду? - person Gregable; 19.02.2014
comment
Возможно, вы могли бы реализовать что-то подобное с syscall.Select: дело в том, что если у вас есть два таймера, которые истекают через 1 и 2 секунды соответственно, вам не нужны две горутины для их пробуждения: вместо этого вы можете иметь одну горутину, которая ждет 1 секунду, а затем будит первый таймер, а затем ждет еще одну секунду, чтобы разбудить второй. Управление такой структурой данных может быть немного сложнее, но это выполнимо. - person James Henstridge; 19.02.2014