终止第二个 goroutine
Terminate the second goroutine
我有以下代码片段。
package main
import (
"errors"
"fmt"
"time"
)
func errName(ch chan error) {
for i := 0; i < 10000; i++ {
}
ch <- errors.New("Error name")
close(ch)
}
func errEmail(ch chan error) {
for i := 0; i < 100; i++ {
}
ch <- errors.New("Error email")
close(ch)
}
func main() {
ch := make(chan error)
go errName(ch)
go errEmail(ch)
fmt.Println(<-ch)
//close(ch)
time.Sleep(1000000)
}
如你所见,我在 goroutine 中让两个函数 运行,errName 和 errEmail。我将错误类型的通道作为参数传递。如果其中一个首先完成,它应该通过通道发送错误并关闭它。所以第二个,仍然是 运行ning goroutine,不必再 运行,因为我已经得到了错误,我想终止仍然是 运行ning goroutine。这就是我在上面的示例中试图达到的目的。
当我运行程序时,出现错误
panic: send on closed channel
goroutine 6 [running]:
main.errEmail(0xc0820101e0)
D:/gocode/src/samples/gorountine2.go:24 +0xfd
created by main.main
D:/gocode/src/samples/gorountine2.go:33 +0x74
goroutine 1 [runnable]:
main.main()
D:/gocode/src/samples/gorountine2.go:34 +0xac
exit status 2
我知道,当我删除 close 语句时,它不会 panic,但是 运行ning goroutine 上的通道仍在等待错误引用,这意味着,它白白浪费了内存(等待) .
当其中一个向频道发送错误时,第二个错误我将不再关心,那是我的目标。
使用另一个渠道发出完成信号:
package main
import (
"errors"
"fmt"
"time"
)
func errName(ch chan error, done chan struct{}) {
for i := 0; i < 10000; i++ {
select {
case <-done:
fmt.Println("early return from name")
return
default:
}
}
select {
case: ch <- errors.New("Error name")
default:
}
}
func errEmail(ch chan error, done chan struct{}) {
for i := 0; i < 100; i++ {
select {
case <-done:
fmt.Println("early return from email")
return
default:
}
}
select {
case ch <- errors.New("Error email"):
default:
}
}
func main() {
ch := make(chan error, 1)
done := make(chan struct{})
go errName(ch, done)
go errEmail(ch, done)
fmt.Println(<-ch)
close(done)
time.Sleep(1000000)
}
为了防止丢失的 goroutine 在通道发送时永远阻塞,我创建了容量为 1 的错误通道,并在发送时使用 select:
select {
case ch <- errors.New("Error email"):
default:
}
如果您正在使用不止一级的 goroutine 完成,那么您应该考虑使用 golang/x/net/context Context。
组织此行为的标准方法是使用
package main
import (
"fmt"
"time"
"code.google.com/p/go.net/context"
)
func errName(ctx context.Context, cancel context.CancelFunc) {
for i := 0; i < 10000; i++ {
select {
case <-ctx.Done():
return
default:
}
}
cancel()
}
func errEmail(ctx context.Context, cancel context.CancelFunc) {
for i := 0; i < 100; i++ {
select {
case <-ctx.Done():
return
default:
}
}
cancel()
}
func main() {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
go errName(ctx, cancel)
go errEmail(ctx, cancel)
<-ctx.Done()
if ctx.Err() != nil {
fmt.Println(ctx.Err())
}
time.Sleep(1000000)
}
你可以阅读两篇关于此事的好文章:
Done chan struct{}
提到(或其 context.Context
化身)是惯用的和真正的行为方式。但是避免代码段恐慌的简单方法可以是
import "sync"
var once sync.Once
func errName(ch chan error) {
for i := 0; i < 10000; i++ {
}
once.Do(func() {ch <- errors.New("Error name"); close(ch)}())
}
func errName(ch chan error) {
for i := 0; i < 10000; i++ {
}
once.Do(func() {ch <- errors.New("Error name"); close(ch)}())
}
我有以下代码片段。
package main
import (
"errors"
"fmt"
"time"
)
func errName(ch chan error) {
for i := 0; i < 10000; i++ {
}
ch <- errors.New("Error name")
close(ch)
}
func errEmail(ch chan error) {
for i := 0; i < 100; i++ {
}
ch <- errors.New("Error email")
close(ch)
}
func main() {
ch := make(chan error)
go errName(ch)
go errEmail(ch)
fmt.Println(<-ch)
//close(ch)
time.Sleep(1000000)
}
如你所见,我在 goroutine 中让两个函数 运行,errName 和 errEmail。我将错误类型的通道作为参数传递。如果其中一个首先完成,它应该通过通道发送错误并关闭它。所以第二个,仍然是 运行ning goroutine,不必再 运行,因为我已经得到了错误,我想终止仍然是 运行ning goroutine。这就是我在上面的示例中试图达到的目的。
当我运行程序时,出现错误
panic: send on closed channel
goroutine 6 [running]:
main.errEmail(0xc0820101e0)
D:/gocode/src/samples/gorountine2.go:24 +0xfd
created by main.main
D:/gocode/src/samples/gorountine2.go:33 +0x74
goroutine 1 [runnable]:
main.main()
D:/gocode/src/samples/gorountine2.go:34 +0xac
exit status 2
我知道,当我删除 close 语句时,它不会 panic,但是 运行ning goroutine 上的通道仍在等待错误引用,这意味着,它白白浪费了内存(等待) .
当其中一个向频道发送错误时,第二个错误我将不再关心,那是我的目标。
使用另一个渠道发出完成信号:
package main
import (
"errors"
"fmt"
"time"
)
func errName(ch chan error, done chan struct{}) {
for i := 0; i < 10000; i++ {
select {
case <-done:
fmt.Println("early return from name")
return
default:
}
}
select {
case: ch <- errors.New("Error name")
default:
}
}
func errEmail(ch chan error, done chan struct{}) {
for i := 0; i < 100; i++ {
select {
case <-done:
fmt.Println("early return from email")
return
default:
}
}
select {
case ch <- errors.New("Error email"):
default:
}
}
func main() {
ch := make(chan error, 1)
done := make(chan struct{})
go errName(ch, done)
go errEmail(ch, done)
fmt.Println(<-ch)
close(done)
time.Sleep(1000000)
}
为了防止丢失的 goroutine 在通道发送时永远阻塞,我创建了容量为 1 的错误通道,并在发送时使用 select:
select {
case ch <- errors.New("Error email"):
default:
}
如果您正在使用不止一级的 goroutine 完成,那么您应该考虑使用 golang/x/net/context Context。
组织此行为的标准方法是使用
package main
import (
"fmt"
"time"
"code.google.com/p/go.net/context"
)
func errName(ctx context.Context, cancel context.CancelFunc) {
for i := 0; i < 10000; i++ {
select {
case <-ctx.Done():
return
default:
}
}
cancel()
}
func errEmail(ctx context.Context, cancel context.CancelFunc) {
for i := 0; i < 100; i++ {
select {
case <-ctx.Done():
return
default:
}
}
cancel()
}
func main() {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
go errName(ctx, cancel)
go errEmail(ctx, cancel)
<-ctx.Done()
if ctx.Err() != nil {
fmt.Println(ctx.Err())
}
time.Sleep(1000000)
}
你可以阅读两篇关于此事的好文章:
Done chan struct{}
提到(或其 context.Context
化身)是惯用的和真正的行为方式。但是避免代码段恐慌的简单方法可以是
import "sync"
var once sync.Once
func errName(ch chan error) {
for i := 0; i < 10000; i++ {
}
once.Do(func() {ch <- errors.New("Error name"); close(ch)}())
}
func errName(ch chan error) {
for i := 0; i < 10000; i++ {
}
once.Do(func() {ch <- errors.New("Error name"); close(ch)}())
}