gmlock updates, add more unit test cases for gmlock

This commit is contained in:
John
2019-01-18 11:30:52 +08:00
parent 0e39400dd0
commit 9e99e88d27
7 changed files with 202 additions and 39 deletions

View File

@ -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)
}
// 解除基于内存锁的读锁

View File

@ -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失败则返回falsetry==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失败则返回falsetry==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
}

View File

@ -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

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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",

View File

@ -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()