我有一个goroutine,它调用一个方法,并在一个通道上传递返回值:
ch := make(chan int, 100) go func(){ for { ch <- do_stuff() } }()
我该怎么阻止这样一个goroutine呢?
beq87vna1#
一般来说,你会给goroutine传递一个(可能是单独的)信号通道,这个信号通道用来在你想要goroutine停止的时候把一个值推到里面,goroutine会定期轮询这个通道,一旦检测到信号,它就会退出。
quit := make(chan bool) go func() { for { select { case <- quit: return default: // Do other stuff } } }() // Do stuff // Quit goroutine quit <- true
vojdkbi02#
**EDIT:**我匆忙地写下了这个答案,然后才意识到你的问题是关于发送值到goroutine中的chan,下面的方法既可以和上面建议的额外chan一起使用,也可以利用你已经拥有的chan是双向的这一事实,你可以只使用一个......
如果goroutine的存在仅仅是为了处理chan中的元素,那么你可以使用内置的“close”和特殊的接收表单。也就是说,一旦你发送完了chan上的项目,你就关闭它,然后在goroutine中,你会得到一个额外的参数给receive操作符,用来显示通道是否已经关闭。下面是一个完整的例子(等待组用来确保进程一直持续到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() }
6ljaweal3#
一般来说,你可以在goroutine中创建一个通道并接收一个停止信号。在本例中有两种创建频道方法。1.渠道1.上下文。在示例中,我将演示context.WithCancel第一个演示使用channel:
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:
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") }
wdebmtf24#
你不能从外部杀死一个goroutine,你可以给它发信号让它停止使用一个通道,但是goroutine上没有句柄来做任何类型的 meta管理,goroutine是用来合作解决问题的,所以杀死一个行为不端的goroutine几乎永远都不是一个合适的回应,如果你想要隔离以获得健壮性,你可能需要一个进程。
2j4z5cfb5#
我知道这个答案已经被接受了,但是我想我还是要把我的2美分投入进去。我喜欢使用tomb包。它基本上是一个suped up退出通道,但是它也做了一些很好的事情,比如回传错误。在控制下的例程仍然有检查远程终止信号的责任。但是,如果一个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
tzxcd3kk6#
就我个人而言,我更喜欢在goroutine的通道中使用range: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() }
戴夫写了一个伟大的职位关于这一点:http://dave.cheney.net/2013/04/30/curious-channels.
iqih9akk7#
我将提供一种与这里提供的方法略有不同的方法。我假设需要停止的goroutine正在执行一些与其他goroutines完全无关的工作,这些工作将由default select case表示:
goroutine
goroutines
default select case
default: fmt.Println("working") time.Sleep(1 * time.Second)
另一个goroutine(在我的例子中是main)决定停止正在执行某些工作的goroutine。您不能真正终止goroutine。即使您可以终止goroutine,这也是一个坏主意,因为它可能使goroutine处于不希望的状态。因此,我们必须使用信道来传达有人正在向goroutine发信号以停止。
main
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完全停止。
bxjv4tth8#
我修改了邹英的答案,使之成为一个可停止的计时器。希望它能帮助其他人。
func main() { timeOut := make(chan bool) stopTimer := false //10 sec timer - go-routine go func() { var counter = 0 for { if stopTimer{ timeOut <- false return }else { fmt.Printf("timer: %d sec\n", counter) time.Sleep(1 * time.Second) counter++ if counter >= 10 { timeOut <- true return } } } }() go func() { time.Sleep(8 * time.Second) //change 8 to 11 or more to time-out the timer stopTimer = true }() b := <-timeOut fmt.Printf("time-out ,%v\n", b) }
pdkcd3nj9#
我使用close(quitCh)执行以下操作:使用close(),它将广播到所有监听通道以退出,在本例中,所有go例程都在监听退出ch。
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") }
9条答案
按热度按时间beq87vna1#
一般来说,你会给goroutine传递一个(可能是单独的)信号通道,这个信号通道用来在你想要goroutine停止的时候把一个值推到里面,goroutine会定期轮询这个通道,一旦检测到信号,它就会退出。
vojdkbi02#
**EDIT:**我匆忙地写下了这个答案,然后才意识到你的问题是关于发送值到goroutine中的chan,下面的方法既可以和上面建议的额外chan一起使用,也可以利用你已经拥有的chan是双向的这一事实,你可以只使用一个......
如果goroutine的存在仅仅是为了处理chan中的元素,那么你可以使用内置的“close”和特殊的接收表单。
也就是说,一旦你发送完了chan上的项目,你就关闭它,然后在goroutine中,你会得到一个额外的参数给receive操作符,用来显示通道是否已经关闭。
下面是一个完整的例子(等待组用来确保进程一直持续到goroutine完成):
6ljaweal3#
一般来说,你可以在goroutine中创建一个通道并接收一个停止信号。
在本例中有两种创建频道方法。
1.渠道
1.上下文。在示例中,我将演示
context.WithCancel
第一个演示使用
channel
:第二个演示使用
context
:wdebmtf24#
你不能从外部杀死一个goroutine,你可以给它发信号让它停止使用一个通道,但是goroutine上没有句柄来做任何类型的 meta管理,goroutine是用来合作解决问题的,所以杀死一个行为不端的goroutine几乎永远都不是一个合适的回应,如果你想要隔离以获得健壮性,你可能需要一个进程。
2j4z5cfb5#
我知道这个答案已经被接受了,但是我想我还是要把我的2美分投入进去。我喜欢使用tomb包。它基本上是一个suped up退出通道,但是它也做了一些很好的事情,比如回传错误。在控制下的例程仍然有检查远程终止信号的责任。但是,如果一个goroutine的“id”被删除了,那是不可能的。行为不端(即:陷入无限循环)。
下面是我测试过的一个简单的例子:
输出应如下所示:
tzxcd3kk6#
就我个人而言,我更喜欢在goroutine的通道中使用range:
https://play.golang.org/p/qt48vvDu8cd
戴夫写了一个伟大的职位关于这一点:http://dave.cheney.net/2013/04/30/curious-channels.
iqih9akk7#
我将提供一种与这里提供的方法略有不同的方法。
我假设需要停止的
goroutine
正在执行一些与其他goroutines
完全无关的工作,这些工作将由default select case
表示:另一个
goroutine
(在我的例子中是main
)决定停止正在执行某些工作的goroutine
。您不能真正终止goroutine
。即使您可以终止goroutine
,这也是一个坏主意,因为它可能使goroutine
处于不希望的状态。因此,我们必须使用信道来传达有人正在向goroutine
发信号以停止。由于
goroutine
会持续执行一些工作,我们将使用循环来表示,当停止信号发出时,goroutine
会跳出循环。我们可以使用另一个通道来通知
main
goroutine已经停止,下面是完整的例子:main
线程派生一个goroutine
执行某项工作一段时间(在本例中为5秒),当时间到期时,它向goroutine
发送停止信号,并等待它,直到goroutine
完全停止。bxjv4tth8#
我修改了邹英的答案,使之成为一个可停止的计时器。希望它能帮助其他人。
pdkcd3nj9#
我使用close(quitCh)执行以下操作:使用close(),它将广播到所有监听通道以退出,在本例中,所有go例程都在监听退出
ch
。