如何停止一个 goroutine

发布于 2024-11-26 03:55:19 字数 175 浏览 1 评论 0原文

我有一个调用方法并在通道上传递返回值的 goroutine:

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

如何停止这样的 goroutine?

I have a goroutine that calls a method, and passes returned value on a channel:

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

How do I stop such a goroutine?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

夜雨飘雪 2024-12-03 03:55:19

通常,您向 goroutine 传递一个(可能是单独的)信号通道。当您希望 goroutine 停止时,该信号通道用于推送一个值。 Goroutine 定期轮询通道。一旦检测到信号,它就会退出。

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

// Do stuff

// Quit goroutine
quit <- true

Typically, you pass the goroutine a (possibly separate) signal channel. That signal channel is used to push a value into when you want the goroutine to stop. The goroutine polls that channel regularly. 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
乖不如嘢 2024-12-03 03:55:19

一般来说,您可以创建一个通道并在 goroutine 中接收停止信号。

本例中有两种创建通道的方法。

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

Generally, you could create a channel and receive a stop signal in the goroutine.

There two way to create channel in this example.

  1. channel

  2. context. In the example I will demo context.WithCancel

The first demo, use 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:

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")
}
留蓝 2024-12-03 03:55:19

编辑: 我匆忙写下了这个答案,然后才意识到你的问题是关于向 goroutine 内的 chan 发送值。下面的方法可以与上面建议的附加 chan 一起使用,或者使用您已经拥有的 chan 是双向的这一事实,您可以只使用一个...

如果您的 goroutine 仅用于处理来自 chan 的物品,您可以使用“关闭”内置功能和通道的特殊接收表单。

也就是说,一旦您在 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()
}

EDIT: I wrote this answer up in haste, before realizing that your question is about sending values to a chan inside a goroutine. 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...

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.

That is, once you're done sending items on the chan, you close it. Then inside your goroutine you get an extra parameter to the receive operator that shows whether the channel has been closed.

Here is a complete example (the waitgroup is used to make sure that the process continues until the goroutine completes):

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()
}
葮薆情 2024-12-03 03:55:19

你不能从外部杀死 goroutine。您可以向 Goroutine 发出信号以停止使用通道,但 Goroutine 没有句柄来执行任何类型的元管理。 Goroutine 的目的是合作解决问题,因此杀死一个行为不端的 Goroutine 几乎永远都不是一个足够的反应。如果您想要隔离以实现稳健性,您可能需要一个过程。

You can't kill a goroutine from outside. You can signal a goroutine to stop using a channel, but there's no handle on goroutines to do any sort of meta management. Goroutines are intended to cooperatively solve problems, so killing one that is misbehaving would almost never be an adequate response. If you want isolation for robustness, you probably want a process.

被翻牌 2024-12-03 03:55:19

我知道这个答案已经被接受,但我想我应该投入 2 美分。我喜欢使用 tomb 包。它基本上是一个升级的退出通道,但它也做了一些很好的事情,比如传回任何错误。受控制的例程仍然有责任检查远程终止信号。据我所知,如果它行为不当(即:陷入无限循环),则不可能获得 Goroutine 的“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

I know this answer has already been accepted, but I thought I'd throw my 2cents in. I like to use the tomb package. It's basically a suped up quit channel, but it does nice things like pass back any errors as well. 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).

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
为你拒绝所有暧昧 2024-12-03 03:55:19

就我个人而言,我想在 goroutine 中的通道上使用范围:

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 就此写了一篇很棒的文章:http://dave.cheney.net/2013/04/30/curious-channels

Personally, I'd like to use range on a channel in a goroutine:

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.

乖不如嘢 2024-12-03 03:55:19

我使用 close(quitCh) 执行以下操作。使用 close() 它将向所有侦听通道广播退出,在这种情况下,所有 go 例程都在侦听退出 ch

package main

import (
    "fmt"
    "sync"
    "time"
)

func routine(ch chan struct{}, wg *sync.WaitGroup, id int) {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-ch:
            wg.Done()
            fmt.Println(id, "quiting")
            return
        case <-ticker.C:
            fmt.Println(id, "do your stuff")
        }
    }

}

func main() {

    var wg sync.WaitGroup

    c := make(chan struct{}, 1)
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go routine(c, &wg, i)
    }

    <-time.After(time.Second * 2)
    close(c)
    fmt.Println("waiting")
    wg.Wait()

    fmt.Println("Done")

}
 

I do the following, using close(quitCh). Using close() it will broadcast to all listening channels to exit, in this case, all the go routines are listening for the quit ch.

package main

import (
    "fmt"
    "sync"
    "time"
)

func routine(ch chan struct{}, wg *sync.WaitGroup, id int) {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-ch:
            wg.Done()
            fmt.Println(id, "quiting")
            return
        case <-ticker.C:
            fmt.Println(id, "do your stuff")
        }
    }

}

func main() {

    var wg sync.WaitGroup

    c := make(chan struct{}, 1)
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go routine(c, &wg, i)
    }

    <-time.After(time.Second * 2)
    close(c)
    fmt.Println("waiting")
    wg.Wait()

    fmt.Println("Done")

}
 

等你爱我 2024-12-03 03:55:19

我将提供一种与此处提供的方法略有不同的方法。

我假设需要停止的 Goroutine 正在执行一些与其他 Goroutine 完全无关的工作。该工作将由默认选择情况表示:

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

另一个 goroutine (在我的示例中是 main)决定它应该停止 < code>goroutine 正在执行一些工作。你无法真正杀死goroutine。即使可以,这也是一个坏主意,因为它可能会使 goroutine 处于不希望的状态。因此,我们必须使用通道来传达有人正在向 Goroutine 发出停止信号。

stop := make(chan struct{})

由于goroutine将持续执行一些工作。我们将使用一个循环来表示它。当发送停止信号时,goroutine 就会跳出循环。

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

我们可以使用另一个通道向 main 表明 goroutine 已经停止。下面是完整的示例:

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
}

main 线程生成一个 goroutine 来执行某些工作一段时间(在本例中为 5 秒)。当时间到期时,它会向 Goroutine 发送停止信号并等待,直到 Goroutine 完全停止。

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. That work will be represented by the 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. You cannot really kill the goroutine. Even if you could it would be a bad idea because it could leave the goroutine in an undesired state. So, we have to use a channel to communicate that someone is signaling to the goroutine to stop.

stop := make(chan struct{})

Since the goroutine will be continuously performing some work. We will use a loop to represent that. And when the stop signal is sent, the goroutine breaks out of the loop.

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. 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). When the time expires, it sends a stop signal to the goroutine and waits for it until the goroutine is fully stopped.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文