diff --git a/safeChan/safeChan.go b/safeChan/safeChan.go index b314e04..cdab70a 100644 --- a/safeChan/safeChan.go +++ b/safeChan/safeChan.go @@ -6,6 +6,13 @@ import ( "sync" ) +/* + 原生chan主张谁申请谁负责关闭。但实际项目中权责往往没有这么清晰。 + 有时候会出现多个协程对同一个chan写入的情况,此时chan被某一个协程关闭,另一个正在写入会导致panic。 + 如果对原生chan关闭多次也会panic。原生chan更适合一写一读或多读的场景,生产者负责chan生命周期,消费者只负责读。 + SafeChan可以多写多读多关闭而不会panic。 +*/ + type ByteChan = SafeChan[[]byte] type SafeChan[T any] struct { diff --git a/safeChan/safeChan_test.go b/safeChan/safeChan_test.go index 8bd7e5e..be74e68 100644 --- a/safeChan/safeChan_test.go +++ b/safeChan/safeChan_test.go @@ -1,16 +1,39 @@ package safeChan import ( + "github.com/fox/fox/ksync" + "github.com/fox/fox/log" "testing" + "time" ) -func TestSafeChan(t *testing.T) { - ch := NewSafeChan[string](12) +func initLog() { + log.Open("test.log", log.DebugL) +} - // go func() { - _ = ch.Write("hello") - t.Log("write hello. 剩余数量:", ch.Size()) - // }() +func OriginChanPanic(t *testing.T) { + och := make(chan string, 10) + go func() { + och <- "hello" + t.Log("write hello") + close(och) + //close(och) // 多次关闭也会panic + }() + + time.Sleep(time.Millisecond) + t.Log("origin chan was closed") + + ksync.GoSafe(func() { + och <- "world" + }, nil) +} + +func SafeChanNoPanic(t *testing.T) { + ch := NewSafeChan[string](12) + go func() { + _ = ch.Write("hello") + t.Log("write hello. 剩余数量:", ch.Size()) + }() ch.Close() if err := ch.Write("zzz"); err != nil { t.Log("write zzz err.", err) @@ -20,9 +43,6 @@ func TestSafeChan(t *testing.T) { for { select { - case <-ch.ctx.Done(): - t.Log("done") - breakNum++ case v, ok := <-ch.Reader(): if ok { t.Log("read", v, " 剩余数量:", ch.Size()) @@ -41,3 +61,12 @@ func TestSafeChan(t *testing.T) { } } + +func TestSafeChan(t *testing.T) { + initLog() + OriginChanPanic(t) + time.Sleep(time.Second * 1) + SafeChanNoPanic(t) + // 防止主协程过早退出,导致go协程里无法打印 + time.Sleep(time.Second * 1) +}