一、简介
golang中各种状态下channel(管道)的读、写、close操作
二、结论
channel状态 | 读 | 写 | close |
---|---|---|---|
close | 零值 | panic | panic |
nil | 永久阻塞(deadlock) | 永久阻塞(deadlock) | panic |
buffer满 | 正常 | 永久阻塞(deadlock) | 正常 |
buffer空 | 永久阻塞(deadlock) | 正常 | 正常 |
三、验证
创建两个go文件
管道状态文件:chan_state.go
测试文件:chan_state_test.go
命令行执行
go test -v
结果见第四部分
chan_state.go
package channel
import "fmt"
type BaseChan interface {
Read()
Write()
Close()
}
type BasicActionChan struct {
ch chan int
}
func (ch *BasicActionChan) Read() {
value := <-ch.ch
fmt.Println("read success", value)
}
func (ch *BasicActionChan) Write() {
ch.ch <- 1
fmt.Println("write success")
}
func (ch *BasicActionChan) Close() {
close(ch.ch)
}
type CloseChan struct {
BasicActionChan
}
func NewCloseChan() BaseChan {
ch := &CloseChan{BasicActionChan{ch: make(chan int)}}
close(ch.ch)
return ch
}
type NilChan struct {
BasicActionChan
}
func NewNilChan() BaseChan {
ch := &NilChan{BasicActionChan{ch: nil}}
return ch
}
type FullBufferChan struct {
BasicActionChan
}
func NewFullBufferChan() BaseChan {
ch := &FullBufferChan{BasicActionChan{ch: make(chan int, 1)}}
ch.ch <- 1
return ch
}
type EmptyBufferChan struct {
BasicActionChan
}
func NewEmptyBufferChan() BaseChan {
ch := &EmptyBufferChan{BasicActionChan{ch: make(chan int, 1)}}
return ch
}
chan_state_test.go
package channel
import (
"context"
"fmt"
"testing"
"time"
)
type Case struct {
Name string
Ch BaseChan
Action func(baseChan BaseChan)
}
func helper(t *testing.T, c *Case) {
t.Helper()
t.Run(c.Name, func(tt *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
done := make(chan struct{})
panicCh := make(chan struct{})
go func() {
defer func() {
if err := recover(); err != nil {
fmt.Println("panic ", err)
panicCh <- struct{}{}
}
}()
c.Action(c.Ch)
done <- struct{}{}
}()
select {
case <-ctx.Done():
fmt.Println("timeout ")
case <-panicCh:
case <-done:
fmt.Println("success")
}
})
}
func TestNewCloseChan(t *testing.T) {
helper(t, &Case{Name: "close chan read", Ch: NewCloseChan(), Action: func(baseChan BaseChan) {
baseChan.Read()
}})
helper(t, &Case{Name: "close chan write", Ch: NewCloseChan(), Action: func(baseChan BaseChan) {
baseChan.Write()
}})
helper(t, &Case{Name: "close chan close", Ch: NewCloseChan(), Action: func(baseChan BaseChan) {
baseChan.Close()
}})
}
func TestNewNilChan(t *testing.T) {
helper(t, &Case{Name: "nil chan read", Ch: NewNilChan(), Action: func(baseChan BaseChan) {
baseChan.Read()
}})
helper(t, &Case{Name: "nil chan write", Ch: NewNilChan(), Action: func(baseChan BaseChan) {
baseChan.Write()
}})
helper(t, &Case{Name: "nil chan close", Ch: NewNilChan(), Action: func(baseChan BaseChan) {
baseChan.Close()
}})
}
func TestNewFullBufferChan(t *testing.T) {
helper(t, &Case{Name: "full chan read", Ch: NewFullBufferChan(), Action: func(baseChan BaseChan) {
baseChan.Read()
}})
helper(t, &Case{Name: "full chan write", Ch: NewFullBufferChan(), Action: func(baseChan BaseChan) {
baseChan.Write()
}})
helper(t, &Case{Name: "full chan close", Ch: NewFullBufferChan(), Action: func(baseChan BaseChan) {
baseChan.Close()
}})
}
func TestNewEmptyBufferChan(t *testing.T) {
helper(t, &Case{Name: "empty chan read", Ch: NewEmptyBufferChan(), Action: func(baseChan BaseChan) {
baseChan.Read()
}})
helper(t, &Case{Name: "empty chan write", Ch: NewEmptyBufferChan(), Action: func(baseChan BaseChan) {
baseChan.Write()
}})
helper(t, &Case{Name: "empty chan close", Ch: NewEmptyBufferChan(), Action: func(baseChan BaseChan) {
baseChan.Close()
}})
}