[go] 고 루틴을 중지하는 방법

메서드를 호출하고 채널에서 반환 된 값을 전달하는 고 루틴이 있습니다.

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

그런 고 루틴을 어떻게 막을 수 있습니까?



답변

편집 : 귀하의 질문이 goroutine 내부의 chan에 값을 보내는 것에 관한 것임을 깨닫기 전에이 답변을 서둘러 작성했습니다. 아래의 접근 방식은 위에서 제안한 추가 채널과 함께 사용하거나 이미 가지고있는 채널이 양방향이라는 사실을 사용하여 사용할 수 있습니다.

고 루틴이 채널에서 나오는 항목을 처리하기 위해서만 존재하는 경우 “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()
}


답변

일반적으로 고 루틴 a (별도의) 신호 채널을 전달합니다. 이 신호 채널은 고 루틴이 중지되기를 원할 때 값을 푸시하는 데 사용됩니다. 고 루틴은 해당 채널을 정기적으로 폴링합니다. 신호를 감지하는 즉시 종료됩니다.

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

// Do stuff

// Quit goroutine
quit <- true


답변

외부에서 고 루틴을 죽일 수는 없습니다. 고 루틴에 채널 사용을 중지하도록 신호를 보낼 수 있지만 고 루틴에 대한 핸들은 어떤 종류의 메타 관리도 수행 할 수 없습니다. 고 루틴은 협력 적으로 문제를 해결하기위한 것이므로 오작동하는 문제를 죽이는 것은 거의 적절한 대응이 아닙니다. 견고성을 위해 격리를 원한다면 아마도 프로세스가 필요할 것입니다.


답변

일반적으로 채널을 생성하고 고 루틴에서 정지 신호를 수신 할 수 있습니다.

이 예에서 채널을 만드는 방법에는 두 가지가 있습니다.

  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")
}


답변

이 대답은 이미 받아 들여 졌다는 것을 알고 있지만 2 센트를 넣을 것이라고 생각했습니다. 저는 무덤 패키지 를 사용하는 것을 좋아 합니다. 기본적으로 지원 종료 채널이지만 오류를 다시 전달하는 것과 같은 좋은 기능을 수행합니다. 제어되는 루틴은 여전히 ​​원격 킬 신호를 확인하는 책임이 있습니다. Afaik 고 루틴의 “id”를 가져 와서 오작동하는 경우 죽일 수 없습니다 (예 : 무한 루프에 갇혀 있음).

다음은 내가 테스트 한 간단한 예입니다.

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


답변

개인적으로 고 루틴의 채널에서 범위를 사용하고 싶습니다.

https://play.golang.org/p/qt48vvDu8cd

Dave는 이에 대한 훌륭한 게시물을 작성했습니다 : http://dave.cheney.net/2013/04/30/curious-channels .


답변