package main
import (
"context"
"errors"
"fmt"
"sync"
"time"
)
type Detail struct {
ID string
}
func Get(ctx context.Context, id string) (*Detail, error) {
//you can call this function directly
time.Sleep(time.Second * 3)
if id == "3" {
return nil, errors.New("error id is 3")
}
return &Detail{ID: id}, nil
}
func GetAll(ctx context.Context, ids []string) (map[string]*Detail, error) {
var swg sync.WaitGroup
result := make(map[string]*Detail, len(ids))
detailChan := make(chan *Detail, 3)
doneChan := make(chan struct{}, 1)
errChan := make(chan error, 1)
defer close(detailChan)
defer close(doneChan)
defer close(errChan)
ctx, cancel := context.WithCancel(ctx)
for _, value := range ids {
swg.Add(1)
go func(ctx context.Context, v string) {
defer swg.Done()
res, err := Get(ctx, v)
if err != nil {
fmt.Println("get error ", err, v)
errChan <- err
return
}
detailChan <- res
/*
select {
case <-ctx.Done():
return
default:
detailChan <- res
}
*/
}(ctx, value)
}
go func() {
for value := range detailChan {
fmt.Println("range ", value)
result[value.ID] = value
}
}()
go func() {
swg.Wait()
doneChan <- struct{}{}
}()
select {
case err := <-errChan:
fmt.Println("select error:", err)
cancel()
return nil, err
case <-doneChan:
fmt.Println("select done")
}
return result, nil
}
func main() {
str := []string{"1", "2", "3", "4", "5", "6"}
GetAll(context.Background(), str)
fmt.Println("end")
}
当执行 cancel() 的时候,会关闭 detailChan,但是 goroutine 仍然会执行,并向 detailChan 中写数据,导致 panic。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.