mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
gmlock updates, add more unit test cases for gmlock
This commit is contained in:
@ -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)
|
||||
}
|
||||
|
||||
// 解除基于内存锁的读锁
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
94
g/os/gmlock/gmlock_unit_lock_test.go
Normal file
94
g/os/gmlock/gmlock_unit_lock_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
61
g/os/gmlock/gmlock_unit_rlock_test.go
Normal file
61
g/os/gmlock/gmlock_unit_rlock_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
@ -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",
|
||||
|
||||
@ -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()
|
||||
|
||||
Reference in New Issue
Block a user