diff --git a/README.md b/README.md new file mode 100644 index 0000000..aaf535d --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Coding lab + +> Coding GYM + +## 更新记录 + +2023-09-18 + +- [ ] Channel 实现读写锁:来自本人参加百度 ACG 面试,彼时对于并发控制的理解颇为纸上谈兵,未能表现实为遗憾。 + 我对用 channel 模拟这个复杂的任务十分有兴趣,因为在这个过程中 channel 将会扮演非常重要的角色(传递信号量),我就是想通过此达到加深理解的作用 \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4256ad1 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module lab.evlic.cn/go/coding-lab + +go 1.21.0 + +require github.com/smartystreets/goconvey v1.8.1 + +require ( + github.com/gopherjs/gopherjs v1.17.2 // indirect + github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/smarty/assertions v1.15.0 // indirect + lab.evlic.cn/go/pkg v1.0.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..734da82 --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= +github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= +github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= +lab.evlic.cn/go/pkg v0.0.0-20230918075852-9ca9becf0967 h1:Jk4Io04lRwRtTo56GDahstQQksv3ZzglK6QxUarpakY= +lab.evlic.cn/go/pkg v0.0.0-20230918075852-9ca9becf0967/go.mod h1:hvaZsvZvj+ly1Fz59x/d/HyyTAL33T44yTjQx/T/SZ8= +lab.evlic.cn/go/pkg v1.0.0 h1:IuuGjOLTEG9wAWsm4jXh2L7SN6vCJahHOUGAjZ18uno= +lab.evlic.cn/go/pkg v1.0.0/go.mod h1:hvaZsvZvj+ly1Fz59x/d/HyyTAL33T44yTjQx/T/SZ8= diff --git a/sync/rwmutex.go b/sync/rwmutex.go new file mode 100644 index 0000000..b99e501 --- /dev/null +++ b/sync/rwmutex.go @@ -0,0 +1,9 @@ +package sync + +import "sync" + +type RWLocker interface { + sync.Locker + RLock() + RUnlock() +} diff --git a/sync/rwmutex_test.go b/sync/rwmutex_test.go new file mode 100644 index 0000000..5df2773 --- /dev/null +++ b/sync/rwmutex_test.go @@ -0,0 +1,72 @@ +package sync + +import ( + "sync" + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" +) + +func newSyncRWMutex() RWLocker { + return &sync.RWMutex{} +} + +func TestChannelRWMutex(t *testing.T) { + testCase := map[string]func() RWLocker{ + "go-src": newSyncRWMutex, + "rw-mutex_v1": NewRWMutexV1, + } + Convey("rw_mutex", t, func() { + for key, newRWMutexFunc := range testCase { + Convey("rw_mutex_"+key, func() { + rwLock := newRWMutexFunc() + var wg sync.WaitGroup + + // 读操作示例 + for i := 0; i < 5; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + rwLock.RLock() + defer rwLock.RUnlock() + //fmt.Printf("Reader %d is reading...\n", id) + time.Sleep(time.Millisecond * 500) + }(i) + } + + // 写操作示例 + for i := 0; i < 2; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + rwLock.Lock() + defer rwLock.Unlock() + //fmt.Printf("Writer %d is writing...\n", id) + time.Sleep(time.Millisecond * 1000) + }(i) + } + + // 等待所有协程完成 + wg.Wait() + }) + } + + }) +} + +func BenchmarkChannelRWMutex(b *testing.B) { + rwLock := NewRWMutexV1() + var wg sync.WaitGroup + + b.ResetTimer() + for i := 0; i < b.N; i++ { + wg.Add(1) + go func() { + defer wg.Done() + rwLock.RLock() + defer rwLock.RUnlock() + }() + } + wg.Wait() +} diff --git a/sync/rwmutex_v1.go b/sync/rwmutex_v1.go new file mode 100644 index 0000000..692eb05 --- /dev/null +++ b/sync/rwmutex_v1.go @@ -0,0 +1,78 @@ +package sync + +import ( + "sync" +) + +/* +ChannelRWMutexV1 + 使用四把锁 +*/ + +type ChannelRWMutexV1 struct { + mu sync.Mutex // 互斥锁 + rm sync.Mutex // 读等待锁 + wm sync.Mutex // 写等待锁 + cmu sync.Mutex // 计数锁 + rn int // 读等待 + wn int // 写等待 +} + +func NewRWMutexV1() RWLocker { + return &ChannelRWMutexV1{} +} + +func (rw *ChannelRWMutexV1) RLock() { + rw.cmu.Lock() + if rw.wn > 0 { + rw.cmu.Unlock() + rw.wm.Lock() + rw.wm.Unlock() + } else { + defer rw.cmu.Unlock() + } + + rw.rn++ + if rw.rn == 1 { + rw.wm.Lock() + } +} + +func (rw *ChannelRWMutexV1) RUnlock() { + rw.cmu.Lock() + defer rw.cmu.Unlock() + rw.rn-- + + if rw.rn == 0 { + rw.wm.Unlock() + } +} + +func (rw *ChannelRWMutexV1) Lock() { + rw.cmu.Lock() + if rw.rn > 0 { + rw.cmu.Unlock() + rw.wm.Lock() + rw.wm.Unlock() + } else { + defer rw.cmu.Unlock() + } + + rw.wn++ + + if rw.wn == 1 { + rw.rm.Lock() + } + rw.mu.Lock() +} + +func (rw *ChannelRWMutexV1) Unlock() { + rw.cmu.Lock() + defer rw.cmu.Unlock() + rw.wn-- + + if rw.wn == 0 { + rw.rm.Unlock() + } + rw.mu.Unlock() +}