как остановить горутин - PullRequest
80 голосов
/ 24 июля 2011

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

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

Как мне остановить такую ​​горутину?

Ответы [ 6 ]

100 голосов
/ 24 июля 2011

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

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

// Do stuff

// Quit goroutine
quit <- true
45 голосов
/ 11 ноября 2011

РЕДАКТИРОВАТЬ: Я написал этот ответ на скорую руку, прежде чем понять, что ваш вопрос о отправке значений Чан внутри Goroutine.Приведенный ниже подход может быть использован либо с дополнительным каналом, как предложено выше, либо с использованием того факта, что у вас уже есть двунаправленный канал, вы можете использовать только один ...

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

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

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

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()
}
25 голосов
/ 28 июля 2011

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

9 голосов
/ 15 ноября 2017

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

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

  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")
}
9 голосов
/ 23 ноября 2013

Я знаю, что этот ответ уже был принят, но я подумал, что я бы добавил 2 цента. По сути, это канал с хорошим выходом, но он делает хорошие вещи, например, возвращает любые ошибки. Подконтрольная программа по-прежнему несет ответственность за проверку сигналов удаленного уничтожения. Афайк, невозможно получить «идентификатор» горутина и убить его, если он плохо себя ведет (например, застрял в бесконечном цикле).

Вот простой пример, который я протестировал:

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
5 голосов
/ 14 мая 2014

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

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

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

...