我有以下代码段。
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中运行。我将参数传递给具有错误类型的通道。如果其中一个先完成,它应该通过通道发送错误并关闭它。所以第二个,仍在运行goroutine,不再运行,因为我已经有错误,我想终止仍在运行的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语句时,它不会出现恐慌,但正在运行的goroutine上的通道仍在等待错误引用,这就是说,它浪费了内存(等待)。
当其中一个人向频道发送错误时,第二个错误我将不再关心,这是我的目标。
答案 0 :(得分:3)
组织此行为的标准方法是使用
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)
}
你可以阅读两篇关于此事的好文章:
答案 1 :(得分:2)
使用另一个频道发出信号:
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 {
case ch <- errors.New("Error email"):
default:
}
如果您正在使用多个级别的goroutine完成,那么您应该考虑使用golang/x/net/context Context。
答案 2 :(得分:1)
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)}())
}