简体   繁体   English

如何停止一个goroutine

[英]How to stop a goroutine

I have a goroutine that calls a method, and passes returned value on a channel:我有一个调用方法的 goroutine,并在通道上传递返回值:

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

How do I stop such a goroutine?我如何停止这样的 goroutine?

Typically, you pass the goroutine a (possibly separate) signal channel.通常,您向 goroutine 传递一个(可能是单独的)信号通道。 That signal channel is used to push a value into when you want the goroutine to stop.当您希望 goroutine 停止时,该信号通道用于推送一个值。 The goroutine polls that channel regularly. goroutine 定期轮询该通道。 As soon as it detects a signal, it quits.一旦检测到信号,它就会退出。

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

// Do stuff

// Quit goroutine
quit <- true

EDIT: I wrote this answer up in haste, before realizing that your question is about sending values to a chan inside a goroutine.编辑:在意识到您的问题是关于将值发送到 goroutine 中的 chan 之前,我匆忙写下了这个答案。 The approach below can be used either with an additional chan as suggested above, or using the fact that the chan you have already is bi-directional, you can use just the one...下面的方法可以与上面建议的附加 chan 一起使用,或者利用您已经拥有的 chan 是双向的事实,您可以只使用一个...

If your goroutine exists solely to process the items coming out of the chan, you can make use of the "close" builtin and the special receive form for channels.如果您的 goroutine 仅用于处理来自 chan 的项目,您可以使用“close”内置函数和特殊的通道接收表单。

That is, once you're done sending items on the chan, you close it.也就是说,一旦你在 chan 上发送完项目,你就关闭它。 Then inside your goroutine you get an extra parameter to the receive operator that shows whether the channel has been closed.然后在你的 goroutine 中,你会得到一个额外的参数给接收操作符,显示通道是否已经关闭。

Here is a complete example (the waitgroup is used to make sure that the process continues until the goroutine completes):这是一个完整的示例(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()
}

You can't kill a goroutine from outside.你不能从外面杀死一个 goroutine。 You can signal a goroutine to stop using a channel, but there's no handle on goroutines to do any sort of meta management.您可以向 goroutine 发出信号以停止使用通道,但是 goroutine 无法进行任何类型的元管理。 Goroutines are intended to cooperatively solve problems, so killing one that is misbehaving would almost never be an adequate response. Goroutines 旨在合作解决问题,因此杀死一个行为不端的人几乎永远不会是一个适当的回应。 If you want isolation for robustness, you probably want a process.如果您想要隔离以提高健壮性,您可能需要一个过程。

Generally, you could create a channel and receive a stop signal in the goroutine.通常,您可以创建一个通道并在 goroutine 中接收一个停止信号。

There two way to create channel in this example.在这个例子中有两种创建频道的方法。

  1. channel渠道

  2. context .上下文 In the example I will demo context.WithCancel在示例中,我将演示context.WithCancel

The first demo, use channel :第一个演示,使用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")
}

The second demo, use context :第二个演示,使用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")
}

I know this answer has already been accepted, but I thought I'd throw my 2cents in. I like to use the tomb package.我知道这个答案已经被接受了,但我想我会投入我的 2cents。我喜欢使用tomb包。 It's basically a suped up quit channel, but it does nice things like pass back any errors as well.它基本上是一个 suped up 退出频道,但它也做一些不错的事情,比如回传任何错误。 The routine under control still has the responsibility of checking for remote kill signals.受控制的例程仍负责检查远程终止信号。 Afaik it's not possible to get an "id" of a goroutine and kill it if it's misbehaving (ie: stuck in an infinite loop). Afaik 不可能获得 goroutine 的“id”并在它行为不端时将其杀死(即:陷入无限循环)。

Here's a simple example which I tested:这是我测试的一个简单示例:

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

The output should look like:输出应如下所示:

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

Personally, I'd like to use range on a channel in a goroutine:就个人而言,我想在 goroutine 的通道上使用 range:

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

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    c := make(chan bool)
    wg.Add(1)
    go func() {
        defer wg.Done()
        for b := range c {
            fmt.Printf("Hello %t\n", b)
        }
    }()
    c <- true
    c <- true
    close(c)
    wg.Wait()
}

Dave has written a great post about this: http://dave.cheney.net/2013/04/30/curious-channels . Dave 写了一篇关于这个的好文章: http : //dave.cheney.net/2013/04/30/curious-channels

I am going to offer a slightly different approach than the ones provided here.我将提供一种与此处提供的方法略有不同的方法。

I am going to assume the goroutine that needs to be stopped is performing some work that is not related at all to other goroutines .我将假设需要停止的goroutine正在执行一些与其他goroutines完全无关的工作。 That work will be represented by the default select case :该工作将由default select case

default:
    fmt.Println("working")
    time.Sleep(1 * time.Second)

Another goroutine (in my example will be the main ) decides that it should stop the goroutine that is performing some work.另一个goroutine (在我的例子中是main )决定它应该停止正在执行一些工作的goroutine You cannot really kill the goroutine .你不能真正杀死goroutine Even if you could it would be a bad idea because it could leave the goroutine in an undesired state.即使可以,这也是一个坏主意,因为它可能会使goroutine处于不希望的状态。 So, we have to use a channel to communicate that someone is signaling to the goroutine to stop.因此,我们必须使用通道来传达有人正在向goroutine发出停止信号。

stop := make(chan struct{})

Since the goroutine will be continuously performing some work.由于goroutine将不断执行一些工作。 We will use a loop to represent that.我们将使用一个循环来表示。 And when the stop signal is sent, the goroutine breaks out of the loop.当停止信号发送时, goroutine跳出循环。

go func() {
L:
    for {
        select {
        case <-stop:
            fmt.Println("stopping")
            break L
        default:
            fmt.Println("working")
            time.Sleep(1 * time.Second)
        }
    }
}()

We can use another channel to indicate to the main that the goroutine has stopped.我们可以使用另一个通道向main指示 goroutine 已停止。 Here's the full example:这是完整的示例:

package main

import (
    "fmt"
    "time"
)

func main() {
    stop := make(chan struct{})
    stopped := make(chan struct{})

    go func() {
    L:
        for {
            select {
            case <-stop:
                fmt.Println("stopping")
                break L
            default:
                fmt.Println("working")
                time.Sleep(1 * time.Second)
            }
        }

        fmt.Println("stopped")
        stopped <- struct{}{}
    }()

    <-time.After(5 * time.Second)
    stop <- struct{}{} // send a signal to stop
    close(stop)
    <-stopped // wait for stop
}

The main thread spawns a goroutine to perform some work for some time (in this case 5 seconds). main线程产生一个goroutine来执行一些工作一段时间(在本例中为 5 秒)。 When the time expires, it sends a stop signal to the goroutine and waits for it until the goroutine is fully stopped.当时间到期时,它会向goroutine发送停止信号并等待它,直到goroutine完全停止。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM