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

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

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

Как остановить такого goroutine?

6 ответов


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

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

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

вот полный пример (waitgroup используется, чтобы убедиться, что процесс продолжается до завершения 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. Goroutine регулярно опрашивает этот канал. Как только он обнаруживает сигнал, он уходит.

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

// Do stuff

// Quit goroutine
quit <- true

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


Я знаю, что этот ответ уже принят,но я думал, что брошу свои 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. контекст. В примере я буду demo 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")
}