From 9e99e88d27c80d609b04bf85d74c61ec6ba19d4c Mon Sep 17 00:00:00 2001 From: John Date: Fri, 18 Jan 2019 11:30:52 +0800 Subject: [PATCH] gmlock updates, add more unit test cases for gmlock --- g/os/gmlock/gmlock.go | 18 +++-- g/os/gmlock/gmlock_locker.go | 36 ++++------ g/os/gmlock/gmlock_mutex.go | 21 +++--- g/os/gmlock/gmlock_unit_lock_test.go | 94 +++++++++++++++++++++++++++ g/os/gmlock/gmlock_unit_rlock_test.go | 61 +++++++++++++++++ g/os/gtime/gtime.go | 9 +++ geg/os/gtimer/gtimer1.go | 2 +- 7 files changed, 202 insertions(+), 39 deletions(-) create mode 100644 g/os/gmlock/gmlock_unit_lock_test.go create mode 100644 g/os/gmlock/gmlock_unit_rlock_test.go diff --git a/g/os/gmlock/gmlock.go b/g/os/gmlock/gmlock.go index 54c6c58f0..29467d3c2 100644 --- a/g/os/gmlock/gmlock.go +++ b/g/os/gmlock/gmlock.go @@ -9,15 +9,19 @@ // 内存锁. package gmlock -var locker = New() +import "time" + +var ( + locker = New() +) // 内存写锁,如果锁成功返回true,失败则返回false;过期时间单位为秒,默认为0表示不过期 -func TryLock(key string, expire...int) bool { +func TryLock(key string, expire...time.Duration) bool { return locker.TryLock(key, expire...) } // 内存写锁,锁成功返回true,失败时阻塞,当失败时表示有其他写锁存在;过期时间单位为秒,默认为0表示不过期 -func Lock(key string, expire...int) { +func Lock(key string, expire...time.Duration) { locker.Lock(key, expire...) } @@ -27,13 +31,13 @@ func Unlock(key string) { } // 内存读锁,如果锁成功返回true,失败则返回false; 过期时间单位为秒,默认为0表示不过期 -func TryRLock(key string, expire...int) bool { - return locker.TryRLock(key, expire...) +func TryRLock(key string) bool { + return locker.TryRLock(key) } // 内存写锁,锁成功返回true,失败时阻塞,当失败时表示有写锁存在; 过期时间单位为秒,默认为0表示不过期 -func RLock(key string, expire...int) { - locker.RLock(key, expire...) +func RLock(key string) { + locker.RLock(key) } // 解除基于内存锁的读锁 diff --git a/g/os/gmlock/gmlock_locker.go b/g/os/gmlock/gmlock_locker.go index 1cecba49e..5c5fcb385 100644 --- a/g/os/gmlock/gmlock_locker.go +++ b/g/os/gmlock/gmlock_locker.go @@ -17,20 +17,20 @@ type Locker struct { m *gmap.StringInterfaceMap } -// 创建一把内存锁使用的底层RWLocker +// 创建一把内存锁, 底层使用的是Mutex func New() *Locker { return &Locker{ m : gmap.NewStringInterfaceMap(), } } -// 内存写锁,如果锁成功返回true,失败则返回false; 过期时间单位为秒,默认为0表示不过期 -func (l *Locker) TryLock(key string, expire...int) bool { +// 内存写锁,如果锁成功返回true,失败则返回false; 过期时间默认为0表示不过期 +func (l *Locker) TryLock(key string, expire...time.Duration) bool { return l.doLock(key, l.getExpire(expire...), true) } -// 内存写锁,锁成功返回true,失败时阻塞,当失败时表示有其他写锁存在;过期时间单位为秒,默认为0表示不过期 -func (l *Locker) Lock(key string, expire...int) { +// 内存写锁,锁成功返回true,失败时阻塞,当失败时表示有其他写锁存在;过期时间默认为0表示不过期 +func (l *Locker) Lock(key string, expire...time.Duration) { l.doLock(key, l.getExpire(expire...), false) } @@ -42,13 +42,13 @@ func (l *Locker) Unlock(key string) { } // 内存读锁,如果锁成功返回true,失败则返回false; 过期时间单位为秒,默认为0表示不过期 -func (l *Locker) TryRLock(key string, expire...int) bool { - return l.doRLock(key, l.getExpire(expire...), true) +func (l *Locker) TryRLock(key string) bool { + return l.doRLock(key, true) } // 内存写锁,锁成功返回true,失败时阻塞,当失败时表示有写锁存在; 过期时间单位为秒,默认为0表示不过期 -func (l *Locker) RLock(key string, expire...int) { - l.doRLock(key, l.getExpire(expire...), false) +func (l *Locker) RLock(key string) { + l.doRLock(key, false) } // 解除基于内存锁的读锁 @@ -59,8 +59,8 @@ func (l *Locker) RUnlock(key string) { } // 获得过期时间,没有设置时默认为0不过期 -func (l *Locker) getExpire(expire...int) int { - e := 0 +func (l *Locker) getExpire(expire...time.Duration) time.Duration { + e := time.Duration(0) if len(expire) > 0 { e = expire[0] } @@ -68,7 +68,7 @@ func (l *Locker) getExpire(expire...int) int { } // 内存写锁,当try==true时,如果锁成功返回true,失败则返回false;try==false时,成功时立即返回,否则阻塞等待 -func (l *Locker) doLock(key string, expire int, try bool) bool { +func (l *Locker) doLock(key string, expire time.Duration, try bool) bool { mu := l.getOrNewMutex(key) ok := true if try { @@ -79,7 +79,7 @@ func (l *Locker) doLock(key string, expire int, try bool) bool { if ok && expire > 0 { // 异步goroutine计时处理 wid := mu.wid.Val() - gtimer.AddOnce(time.Duration(expire)*time.Second, func() { + gtimer.AddOnce(expire, func() { if wid == mu.wid.Val() { mu.Unlock() } @@ -89,7 +89,7 @@ func (l *Locker) doLock(key string, expire int, try bool) bool { } // 内存读锁,当try==true时,如果锁成功返回true,失败则返回false;try==false时,成功时立即返回,否则阻塞等待 -func (l *Locker) doRLock(key string, expire int, try bool) bool { +func (l *Locker) doRLock(key string, try bool) bool { mu := l.getOrNewMutex(key) ok := true if try { @@ -97,14 +97,6 @@ func (l *Locker) doRLock(key string, expire int, try bool) bool { } else { mu.RLock() } - if ok && expire > 0 { - rid := mu.rid.Val() - gtimer.AddOnce(time.Duration(expire)*time.Second, func() { - if rid == mu.rid.Val() { - mu.RUnlock() - } - }) - } return ok } diff --git a/g/os/gmlock/gmlock_mutex.go b/g/os/gmlock/gmlock_mutex.go index a1ab50101..c5c2ccab4 100644 --- a/g/os/gmlock/gmlock_mutex.go +++ b/g/os/gmlock/gmlock_mutex.go @@ -7,15 +7,13 @@ package gmlock import ( - "sync" "gitee.com/johng/gf/g/container/gtype" - "gitee.com/johng/gf/g/os/gtime" + "sync" ) // 互斥锁对象 type Mutex struct { mu sync.RWMutex - rid *gtype.Int64 // 当前RLock产生的唯一id(主要用于计时RUnlock的校验) wid *gtype.Int64 // 当前Lock产生的唯一id(主要用于计时Unlock的校验) rcount *gtype.Int // RLock次数 wcount *gtype.Int // Lock次数 @@ -24,7 +22,6 @@ type Mutex struct { // 创建一把内存锁使用的底层RWMutex func NewMutex() *Mutex { return &Mutex{ - rid : gtype.NewInt64(), wid : gtype.NewInt64(), rcount : gtype.NewInt(), wcount : gtype.NewInt(), @@ -34,7 +31,7 @@ func NewMutex() *Mutex { func (l *Mutex) Lock() { l.wcount.Add(1) l.mu.Lock() - l.wid.Set(gtime.Nanosecond()) + l.wid.Add(1) } // 安全的Unlock @@ -42,6 +39,9 @@ func (l *Mutex) Unlock() { if l.wcount.Val() > 0 { if l.wcount.Add(-1) >= 0 { l.mu.Unlock() + } else { + // 标准库这里会panic + l.wcount.Add(1) } } } @@ -49,14 +49,16 @@ func (l *Mutex) Unlock() { func (l *Mutex) RLock() { l.rcount.Add(1) l.mu.RLock() - l.rid.Set(gtime.Nanosecond()) } // 安全的RUnlock func (l *Mutex) RUnlock() { if l.rcount.Val() > 0 { - if l.wcount.Add(-1) >= 0 { + if l.rcount.Add(-1) >= 0 { l.mu.RUnlock() + } else { + // 标准库这里会panic + l.rcount.Add(1) } } } @@ -68,8 +70,10 @@ func (l *Mutex) TryLock() bool { // 第二次检查, 保证原子操作 if l.wcount.Add(1) == 1 { l.mu.Lock() - l.wid.Set(gtime.Nanosecond()) + l.wid.Add(1) return true + } else { + l.wcount.Add(-1) } } return false @@ -81,7 +85,6 @@ func (l *Mutex) TryRLock() bool { if l.wcount.Val() == 0 { l.rcount.Add(1) l.mu.RLock() - l.rid.Set(gtime.Nanosecond()) return true } return false diff --git a/g/os/gmlock/gmlock_unit_lock_test.go b/g/os/gmlock/gmlock_unit_lock_test.go new file mode 100644 index 000000000..04665eed4 --- /dev/null +++ b/g/os/gmlock/gmlock_unit_lock_test.go @@ -0,0 +1,94 @@ +// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. + +package gmlock_test + +import ( + "gitee.com/johng/gf/g/container/garray" + "gitee.com/johng/gf/g/os/gmlock" + "gitee.com/johng/gf/g/util/gtest" + "testing" + "time" +) + +func TestLocker_Lock_Unlock(t *testing.T) { + gtest.Case(t, func() { + array := garray.New(0, 0, true) + go func() { + gmlock.Lock("test") + array.Append(1) + time.Sleep(100*time.Millisecond) + array.Append(1) + gmlock.Unlock("test") + }() + go func() { + gmlock.Lock("test") + array.Append(1) + time.Sleep(200*time.Millisecond) + array.Append(1) + gmlock.Unlock("test") + }() + time.Sleep(50*time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(80*time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(100*time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(100*time.Millisecond) + gtest.Assert(array.Len(), 4) + }) +} + +func TestLocker_Lock_Expire(t *testing.T) { + gtest.Case(t, func() { + array := garray.New(0, 0, true) + go func() { + gmlock.Lock("test", 50*time.Millisecond) + array.Append(1) + }() + go func() { + gmlock.Lock("test") + time.Sleep(50*time.Millisecond) + array.Append(1) + gmlock.Unlock("test") + }() + time.Sleep(80*time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(80*time.Millisecond) + gtest.Assert(array.Len(), 2) + }) +} + +func TestLocker_TryLock_Expire(t *testing.T) { + gtest.Case(t, func() { + array := garray.New(0, 0, true) + go func() { + gmlock.Lock("test", 200*time.Millisecond) + array.Append(1) + }() + go func() { + time.Sleep(50*time.Millisecond) + if !gmlock.TryLock("test") { + array.Append(1) + } else { + gmlock.Unlock("test") + } + }() + go func() { + time.Sleep(300*time.Millisecond) + if gmlock.TryLock("test") { + array.Append(1) + gmlock.Unlock("test") + } + }() + time.Sleep(20*time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(80*time.Millisecond) + gtest.Assert(array.Len(), 2) + time.Sleep(350*time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} diff --git a/g/os/gmlock/gmlock_unit_rlock_test.go b/g/os/gmlock/gmlock_unit_rlock_test.go new file mode 100644 index 000000000..aa0f8f3be --- /dev/null +++ b/g/os/gmlock/gmlock_unit_rlock_test.go @@ -0,0 +1,61 @@ +// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. + +package gmlock_test + +import ( + "gitee.com/johng/gf/g/container/garray" + "gitee.com/johng/gf/g/os/gmlock" + "gitee.com/johng/gf/g/util/gtest" + "testing" + "time" +) + +func TestLocker_RLock1(t *testing.T) { + gtest.Case(t, func() { + array := garray.New(0, 0, true) + go func() { + gmlock.RLock("test") + array.Append(1) + time.Sleep(50*time.Millisecond) + array.Append(1) + gmlock.RUnlock("test") + }() + go func() { + time.Sleep(10*time.Millisecond) + gmlock.Lock("test") + array.Append(1) + gmlock.Unlock("test") + }() + time.Sleep(20*time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(80*time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} + +func TestLocker_RLock2(t *testing.T) { + gtest.Case(t, func() { + array := garray.New(0, 0, true) + go func() { + gmlock.Lock("test") + array.Append(1) + time.Sleep(100*time.Millisecond) + gmlock.Unlock("test") + }() + go func() { + time.Sleep(10*time.Millisecond) + gmlock.RLock("test") + array.Append(1) + gmlock.RUnlock("test") + }() + + time.Sleep(20*time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(120*time.Millisecond) + gtest.Assert(array.Len(), 2) + }) +} \ No newline at end of file diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go index 740012e65..4b7cef60e 100644 --- a/g/os/gtime/gtime.go +++ b/g/os/gtime/gtime.go @@ -20,6 +20,15 @@ import ( ) const ( + // 时间间隔缩写 + D = 24*time.Hour + H = time.Hour + M = time.Minute + S = time.Second + MS = time.Millisecond + US = time.Microsecond + NS = time.Nanosecond + // 常用时间格式正则匹配,支持的标准时间格式: // "2017-12-14 04:51:34 +0805 LMT", // "2017-12-14 04:51:34 +0805 LMT", diff --git a/geg/os/gtimer/gtimer1.go b/geg/os/gtimer/gtimer1.go index 46169d477..689b3738d 100644 --- a/geg/os/gtimer/gtimer1.go +++ b/geg/os/gtimer/gtimer1.go @@ -8,7 +8,7 @@ import ( func main() { now := time.Now() - interval := 1400*time.Millisecond + interval := 50*time.Millisecond gtimer.Add(interval, func() { fmt.Println(time.Now(), time.Duration(time.Now().UnixNano() - now.UnixNano())) now = time.Now()