как остановить goroutine


У меня есть goroutine, который вызывает метод и передает возвращаемое значение по каналу:

ch := make(chan int, 100)
go func(){
    for {
        ch <- do_stuff()
    }
}()

Как мне остановить такой goroutine?

6 66

6 ответов:

EDIT:Я написал этот ответ в спешке, прежде чем понял, что ваш вопрос заключается в отправке значений chan внутри goroutine. Подход ниже может быть использован либо с дополнительным Чан, как предложено выше, или с использованием того факта, что Чан У вас уже двунаправленный, вы можете использовать только один...

Если ваш goroutine существует исключительно для обработки элементов, выходящих из chan, вы можете использовать встроенный "close" и специальная форма приема для каналов.

то есть, как только вы закончите отправлять элементы на chan, вы его закроете. Затем внутри вашего goroutine вы получаете дополнительный параметр для оператора приема, который показывает, был ли канал закрыт.

вот полный пример (группа ожидания используется, чтобы убедиться, что процесс продолжается до завершения goroutine):

package main

import "sync"
func main() {
    var wg sync.WaitGroup
    wg.Add(1)

    ch := make(chan int)
    go func() {
        for {
            foo, ok := <- ch
            if !ok {
                println("done")
                wg.Done()
                return
            }
            println(foo)
        }
    }()
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)

    wg.Wait()
}

Как правило, вы передаете goroutine a (возможно, отдельный) сигнальный канал. Этот сигнальный канал используется для ввода значения, когда вы хотите, чтобы goroutine остановился. Опросы горутина, которые регулярно канал. Как только он обнаруживает сигнал, он уходит.

quit := make(chan bool)
go func() {
    for {
        select {
        case <- quit:
            return
        default:
            // Do other stuff
        }
    }
}()

// Do stuff

// Quit goroutine
quit <- true

ты не можешь убить горутина извне. Вы можете сигнализировать goroutine прекратить использование канала, но на goroutines нет дескриптора для какого-либо мета-управления. Goroutines предназначены для совместного решения проблем, поэтому убийство того, кто плохо себя ведет, почти никогда не будет адекватным ответом. Если вы хотите изоляции для надежности, вы, вероятно, хотите процесс.

Я знаю, что этот ответ уже принят, но я думал, что брошу свои 2cents. Мне нравится использовать гроб пакета. Это в основном suped up quit channel, но он делает хорошие вещи, такие как передача любых ошибок. Рутина под контролем по-прежнему несет ответственность за проверку сигналов дистанционного уничтожения. Afaik невозможно получить "идентификатор" goroutine и убить его, если он плохо себя ведет (т. е. застрял в бесконечном цикле).

вот простой пример, который Я проверил:

package main

import (
  "launchpad.net/tomb"
  "time"
  "fmt"
)

type Proc struct {
  Tomb tomb.Tomb
}

func (proc *Proc) Exec() {
  defer proc.Tomb.Done() // Must call only once
  for {
    select {
    case <-proc.Tomb.Dying():
      return
    default:
      time.Sleep(300 * time.Millisecond)
      fmt.Println("Loop the loop")
    }
  }
}

func main() {
  proc := &Proc{}
  go proc.Exec()
  time.Sleep(1 * time.Second)
  proc.Tomb.Kill(fmt.Errorf("Death from above"))
  err := proc.Tomb.Wait() // Will return the error that killed the proc
  fmt.Println(err)
}

результат должен выглядеть так:

# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above

лично я хотел бы использовать диапазон на канале в goroutine:

http://play.golang.org/p/KjG8FLzPoz

Дэйв написал отличный пост об этом:http://dave.cheney.net/2013/04/30/curious-channels.

как правило, вы можете создать канал и получить стоп-сигнал в подпрограмме go.

есть два способа создать канал в этом примере.

  1. канал

  2. контекст. В примере я буду демо context.WithCancel

первая демонстрация, используйте channel:

package main

import "fmt"
import "time"

func do_stuff() int {
    return 1
}

func main() {

    ch := make(chan int, 100)
    done := make(chan struct{})
    go func() {
        for {
            select {
            case ch <- do_stuff():
            case <-done:
                close(ch)
                return
            }
            time.Sleep(100 * time.Millisecond)
        }
    }()

    go func() {
        time.Sleep(3 * time.Second)
        done <- struct{}{}
    }()

    for i := range ch {
        fmt.Println("receive value: ", i)
    }

    fmt.Println("finish")
}

вторая демонстрация, используйте context:

package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    forever := make(chan struct{})
    ctx, cancel := context.WithCancel(context.Background())

    go func(ctx context.Context) {
        for {
            select {
            case <-ctx.Done():  // if cancel() execute
                forever <- struct{}{}
                return
            default:
                fmt.Println("for loop")
            }

            time.Sleep(500 * time.Millisecond)
        }
    }(ctx)

    go func() {
        time.Sleep(3 * time.Second)
        cancel()
    }()

    <-forever
    fmt.Println("finish")
}