Golang 的帮看下,运行的时候 panic,原因是写入了关闭的 channel,请问该如何解决?

2019-09-19 11:47:46 +08:00
 ngnetboy
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。

2720 次点击
所在节点    问与答
21 条回复
visitant
2019-09-19 19:35:47 +08:00
@ngnetboy 这跟你缓存是开了多大根本没关系,关键在于 select 里 case <-ctx.Done() 然后你还有个 default,导致协程根本没被 select blcok,而是被 res 写 chan 的操作 block

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/602103

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX