初始化项目

This commit is contained in:
皮蛋13361098506
2025-01-06 16:01:02 +08:00
commit 1b77f62820
575 changed files with 69193 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
package syncUtil
import (
"fmt"
"runtime/debug"
"sync"
"time"
)
// 写锁对象
type Locker struct {
locking bool
prevStack []byte
mutex sync.Mutex
}
// 内部锁
// 返回值:
// 加锁是否成功
func (this *Locker) lock() bool {
this.mutex.Lock()
defer this.mutex.Unlock()
// 如果已经被锁定,则返回失败
if this.locking {
return false
}
// 否则进行锁定,并返回成功
this.locking = true
// 记录Stack信息
if if_record_stack_info {
this.prevStack = debug.Stack()
}
return true
}
// 尝试加锁,如果在指定的时间内失败,则会返回失败;否则返回成功
// timeout:指定的毫秒数,timeout<=0则将会死等
// 返回值:
// 成功或失败
// 如果失败,返回上一次成功加锁时的堆栈信息
// 如果失败,返回当前的堆栈信息
func (this *Locker) Lock(timeout int) (successful bool, prevStack string, currStack string) {
timeout = getTimeout(timeout)
// 遍历指定的次数(即指定的超时时间)
for i := 0; i < timeout; i++ {
// 如果锁定成功,则返回成功
if this.lock() {
successful = true
break
}
// 如果锁定失败则休眠1ms然后再重试
time.Sleep(time.Millisecond)
}
// 如果时间结束仍然是失败,则返回上次成功的堆栈信息,以及当前的堆栈信息
if successful == false {
if this.prevStack != nil && len(this.prevStack) > 0 {
prevStack = string(this.prevStack)
}
currStack = string(debug.Stack())
}
return
}
// 锁定(死等方式)
func (this *Locker) WaitLock() {
successful, prevStack, currStack := this.Lock(0)
if successful == false {
fmt.Printf("Locker.WaitLock():{PrevStack:%s, currStack:%s}\n", prevStack, currStack)
}
}
// 解锁
func (this *Locker) Unlock() {
this.mutex.Lock()
defer this.mutex.Unlock()
this.locking = false
}
// 创建新的锁对象
func NewLocker() *Locker {
return &Locker{}
}

View File

@@ -0,0 +1,58 @@
package syncUtil
import (
"sync"
)
// 锁工具类
type LockerUtil struct {
// 锁集合
lockerMap map[string]*Locker
// 锁对象
rwMutex sync.RWMutex
}
// 创建新的锁工具类
func NewLockerUtil() *LockerUtil {
return &LockerUtil{
lockerMap: make(map[string]*Locker, 8),
}
}
// 获取锁对象
// lockName:锁名
// 返回值:
// *Locker:锁对象
func (this *LockerUtil) GetLock(lockName string) *Locker {
var lockerObj *Locker
var exists bool
func() {
this.rwMutex.RLock()
defer this.rwMutex.RUnlock()
lockerObj, exists = this.lockerMap[lockName]
}()
if exists {
return lockerObj
}
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
lockerObj, exists = this.lockerMap[lockName]
if !exists {
lockerObj = NewLocker()
this.lockerMap[lockName] = lockerObj
}
return lockerObj
}
// 释放锁对象
// lockName:锁名
func (this *LockerUtil) ReleaseLock(lockName string) {
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
delete(this.lockerMap, lockName)
}

View File

@@ -0,0 +1,27 @@
package syncUtil
import (
"fmt"
"testing"
)
var (
lockerUtilObj = NewLockerUtil()
)
func LockerUtil_TestGetLock(t *testing.T) {
count := 100
for i := 1; i <= count; i++ {
lockerUtilObj.GetLock(fmt.Sprintf("%d", i))
if lockerCount := len(lockerUtilObj.lockerMap); lockerCount != i {
t.Errorf("(GetLock)Expected %d locker, but now got: %d", count, lockerCount)
}
}
for i := count; i > 0; i-- {
lockerUtilObj.ReleaseLock(fmt.Sprintf("%d", i))
if lockerCount := len(lockerUtilObj.lockerMap); lockerCount != i-1 {
t.Errorf("(ReleaseLock)Expected %d locker, but now got: %d", count, lockerCount)
}
}
}

View File

@@ -0,0 +1,98 @@
package syncUtil
import (
"fmt"
"testing"
"time"
)
func TestNewLocker1(t *testing.T) {
count := 1000000
succeedCount := 0
expected := 1000000
goroutineCount := 1
lockerObj := NewLocker()
ch := make(chan bool, goroutineCount)
for i := 0; i < goroutineCount; i++ {
go lockerTest(lockerObj, &succeedCount, count/goroutineCount, ch)
}
for i := 0; i < goroutineCount; i++ {
<-ch
}
if succeedCount != expected {
t.Errorf("Expected %d, but got %d", expected, succeedCount)
}
}
func TestNewLocker2(t *testing.T) {
count := 1000000
succeedCount := 0
expected := 1000000
goroutineCount := 100
lockerObj := NewLocker()
ch := make(chan bool, goroutineCount)
for i := 0; i < goroutineCount; i++ {
go lockerTest(lockerObj, &succeedCount, count/goroutineCount, ch)
}
for i := 0; i < goroutineCount; i++ {
<-ch
}
if succeedCount != expected {
t.Errorf("Expected %d, but got %d", expected, succeedCount)
}
}
func TestNewLocker3(t *testing.T) {
count := 1000000
succeedCount := 0
expected := 1000000
goroutineCount := 10000
lockerObj := NewLocker()
ch := make(chan bool, goroutineCount)
for i := 0; i < goroutineCount; i++ {
go lockerTest(lockerObj, &succeedCount, count/goroutineCount, ch)
}
for i := 0; i < goroutineCount; i++ {
<-ch
}
if succeedCount != expected {
t.Errorf("Expected %d, but got %d", expected, succeedCount)
}
}
func TestNewLocker4(t *testing.T) {
lockerObj := NewLocker()
if successful, _, _ := lockerObj.Lock(100); successful == false {
t.Errorf("Lock should be successful, but now it fails.")
}
if successful, _, _ := lockerObj.Lock(100); successful {
t.Errorf("Lock should be failed, but now it succeeds.")
}
}
func lockerTest(lockerObj *Locker, succeedCount *int, count int, ch chan bool) {
if success, _, _ := lockerObj.Lock(10000); !success {
fmt.Printf("[%v]获取锁超时\n", time.Now())
return
}
defer lockerObj.Unlock()
for i := 0; i < count; i++ {
*succeedCount += 1
}
ch <- true
}

View File

@@ -0,0 +1,171 @@
package syncUtil
import (
"context"
"sync"
"time"
)
// Set the behavier on unlock unlocked mutex
var PanicOnBug = true
// another mutex implementation with TryLock method
type Mutex interface {
Lock()
UnLock()
// TryLock return true if it fetch mutex
TryLock() bool
// TryLockTimeout return true if it fetch mutex, return false if timeout
TryLockTimeout(timeout time.Duration) bool
// TryLockTimeout return true if it fetch mutex, return false if context done
TryLockContext(ctx context.Context) bool
}
func NewMutex() Mutex {
m := &mutex{ch: make(chan struct{}, 1)}
m.ch <- struct{}{}
return m
}
type mutex struct {
ch chan struct{}
}
func (m *mutex) Lock() {
<-m.ch
}
func (m *mutex) UnLock() {
select {
case m.ch <- struct{}{}:
default:
if PanicOnBug {
panic("unlock of unlocked mutex")
}
}
}
func (m *mutex) TryLock() bool {
select {
case <-m.ch:
return true
default:
}
return false
}
func (m *mutex) TryLockTimeout(timeout time.Duration) bool {
tm := time.NewTimer(timeout)
select {
case <-m.ch:
tm.Stop()
return true
case <-tm.C:
}
return false
}
func (m *mutex) TryLockContext(ctx context.Context) bool {
select {
case <-m.ch:
return true
case <-ctx.Done():
}
return false
}
type MutexGroup interface {
Lock(i interface{})
UnLock(i interface{})
UnLockAndFree(i interface{})
// TryLock return true if it fetch mutex
TryLock(i interface{}) bool
// TryLockTimeout return true if it fetch mutex, return false if timeout
TryLockTimeout(i interface{}, timeout time.Duration) bool
// TryLockTimeout return true if it fetch mutex, return false if context done
TryLockContext(i interface{}, ctx context.Context) bool
}
func NewMutexGroup() MutexGroup {
return &mutexGroup{group: make(map[interface{}]*entry)}
}
type mutexGroup struct {
mu sync.Mutex
group map[interface{}]*entry
}
type entry struct {
ref int
mu Mutex
}
func (m *mutexGroup) get(i interface{}, ref int) Mutex {
m.mu.Lock()
defer m.mu.Unlock()
en, ok := m.group[i]
if !ok {
if ref > 0 {
en = &entry{mu: NewMutex()}
m.group[i] = en
} else if PanicOnBug {
panic("unlock of unlocked mutex")
} else {
return nil
}
}
en.ref += ref
return en.mu
}
func (m *mutexGroup) Lock(i interface{}) {
m.get(i, 1).Lock()
}
func (m *mutexGroup) UnLock(i interface{}) {
mu := m.get(i, -1)
if mu != nil {
mu.UnLock()
}
}
func (m *mutexGroup) UnLockAndFree(i interface{}) {
m.mu.Lock()
defer m.mu.Unlock()
en, ok := m.group[i]
if !ok {
if PanicOnBug {
panic("unlock of unlocked mutex")
}
return
}
en.ref--
if en.ref == 0 {
delete(m.group, i)
}
en.mu.UnLock()
}
func (m *mutexGroup) TryLock(i interface{}) bool {
locked := m.get(i, 1).TryLock()
if !locked {
m.get(i, -1)
}
return locked
}
func (m *mutexGroup) TryLockTimeout(i interface{}, timeout time.Duration) bool {
locked := m.get(i, 1).TryLockTimeout(timeout)
if !locked {
m.get(i, -1)
}
return locked
}
func (m *mutexGroup) TryLockContext(i interface{}, ctx context.Context) bool {
locked := m.get(i, 1).TryLockContext(ctx)
if !locked {
m.get(i, -1)
}
return locked
}

View File

@@ -0,0 +1,225 @@
package syncUtil
import (
"context"
"fmt"
"sync"
"testing"
"time"
)
func TestMutex(t *testing.T) {
mu := NewMutex()
mu.Lock()
defer mu.UnLock()
if mu.TryLock() {
t.Errorf("cannot fetch mutex !!!")
}
}
func TestMutexTryLockTimeout(t *testing.T) {
fmt.Println("start")
mu := NewMutex()
mu.Lock()
go func() {
time.Sleep(20 * time.Second)
mu.UnLock()
}()
// if !mu.TryLockTimeout(500 * time.Microsecond) {
// t.Errorf("cannot fetch mutex in 500us !!!")
// }
if !mu.TryLockTimeout(15 * time.Second) {
t.Errorf("should fetch mutex in 5ms !!!")
}
mu.UnLock()
}
func TestMutexUnlockTwice(t *testing.T) {
mu := NewMutex()
mu.Lock()
defer func() {
if x := recover(); x != nil {
if x != "unlock of unlocked mutex" {
t.Errorf("unexpect panic")
}
} else {
t.Errorf("should panic after unlock twice")
}
}()
mu.UnLock()
mu.UnLock()
}
func TestMutexTryLockContext(t *testing.T) {
mu := NewMutex()
ctx, cancel := context.WithCancel(context.Background())
mu.Lock()
go func() {
time.Sleep(10 * time.Millisecond)
cancel()
}()
if mu.TryLockContext(ctx) {
t.Errorf("cannot fetch mutex !!!")
}
}
func BenchmarkMutex(b *testing.B) {
mu := NewMutex()
a := 0
c := 0
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
mu.Lock()
a++
mu.UnLock()
mu.Lock()
c = a
mu.UnLock()
}
})
_ = a
_ = c
}
func TestMutexGroup(t *testing.T) {
mu := NewMutexGroup()
mu.Lock("g")
defer mu.UnLock("g")
if mu.TryLock("g") {
t.Errorf("cannot fetch mutex !!!")
}
}
func TestMutexGroupMutliWaitLock(t *testing.T) {
var (
wg sync.WaitGroup
mu = NewMutexGroup()
cn = 3
)
for i := 0; i < cn; i++ {
wg.Add(1)
go func() {
mu.Lock("h")
time.Sleep(1e7)
mu.UnLock("h")
wg.Done()
}()
}
wg.Wait()
for i := 0; i < cn; i++ {
wg.Add(1)
go func() {
mu.Lock("g")
time.Sleep(1e7)
mu.UnLockAndFree("g")
wg.Done()
}()
}
wg.Wait()
}
func TestMutexGroupUnLockAndFree(t *testing.T) {
var (
wg sync.WaitGroup
mu = NewMutexGroup()
mg = mu.(*mutexGroup)
)
for j := 1; j < 5; j++ {
for i := 0; i < j; i++ {
wg.Add(1)
go func() {
mu.Lock("h")
time.Sleep(1e6)
mu.UnLockAndFree("h")
wg.Done()
}()
}
wg.Wait()
mg.mu.Lock()
if _, ok := mg.group["h"]; ok {
t.Error("h mutex exist after UnLockAndFree")
}
mg.mu.Unlock()
}
}
func TestMutexGroupTryLockFailedAndUnLockAndFree(t *testing.T) {
var (
wg sync.WaitGroup
mu = NewMutexGroup()
mg = mu.(*mutexGroup)
)
for j := 1; j < 5; j++ {
for i := 0; i < j; i++ {
wg.Add(1)
go func() {
if mu.TryLock("h") {
time.Sleep(1e6)
mu.UnLockAndFree("h")
}
wg.Done()
}()
}
wg.Wait()
mg.mu.Lock()
if _, ok := mg.group["h"]; ok {
t.Error("h mutex exist after UnLockAndFree")
}
mg.mu.Unlock()
}
}
func TestMutexGroupTryLockTimeout(t *testing.T) {
mu := NewMutexGroup()
mu.Lock("g")
go func() {
time.Sleep(1 * time.Millisecond)
mu.UnLock("g")
}()
if mu.TryLockTimeout("g", 500*time.Microsecond) {
t.Errorf("cannot fetch mutex in 500us !!!")
}
if !mu.TryLockTimeout("g", 5*time.Millisecond) {
t.Errorf("should fetch mutex in 5ms !!!")
}
mu.UnLock("g")
}
func TestMutexGroupTryLockContext(t *testing.T) {
mu := NewMutexGroup()
ctx, cancel := context.WithCancel(context.Background())
mu.Lock("g")
go func() {
time.Sleep(10 * time.Millisecond)
cancel()
}()
if mu.TryLockContext("g", ctx) {
t.Errorf("cannot fetch mutex !!!")
}
}
func BenchmarkMutexGroup(b *testing.B) {
mu := NewMutexGroup()
a := 0
c := 0
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
mu.Lock("g")
a++
mu.UnLock("g")
mu.Lock("g")
c = a
mu.UnLock("g")
}
})
_ = a
_ = c
}

View File

@@ -0,0 +1,178 @@
/*
通过在RWLocker对象中引入writeProtectEndTime写锁保护结束时间来提高获取写锁的成功率。
当写锁获取失败时,就设置一个写锁保护结束时间,在这段时间内,只允许写锁进行获取,而读锁的获取请求会被拒绝。
通过重置写锁保护结束时间的时机,对写锁的优先级程度进行调整。有两个重置写锁保护结束时间的时机:
1、在成功获取到写锁时:此时重置,有利于下一个写锁需求者在当前写锁持有者处理逻辑时设置保护时间,从而当当前写锁持有者释放锁时,下一个写锁需求者可以立刻获得写锁;
2、在写锁解锁时:此时重置,给了读锁和写锁的需求者同样的机会进行锁的竞争机会;
综上RWLocker可以提供中级别的写锁优先级
1、高级:在获取写锁失败时设置写锁保护结束时间;在获取写锁成功时重置。
2、中级:在获取写锁失败时设置写锁保护结束时间;在释放锁时重置。
3、无:不设置写锁保护时间。
*/
package syncUtil
import (
"fmt"
"runtime/debug"
"sync"
"time"
)
// 读写锁对象
type RWLocker struct {
read int
write int // 使用int而不是bool值的原因是为了与read保持类型的一致
writeProtectEndTime int64 // 写锁保护结束时间。如果当前时间小于该值,则会阻塞读锁请求;以便于提高写锁的优先级,避免连续的读锁导致写锁无法获得;
prevStack []byte
mutex sync.Mutex
}
// 尝试加写锁
// 返回值:加写锁是否成功
func (this *RWLocker) lock() bool {
this.mutex.Lock()
defer this.mutex.Unlock()
// 如果已经被锁定,则返回失败;并且设置写锁保护结束时间;以便于写锁可以优先竞争锁;
if this.write == 1 || this.read > 0 {
this.writeProtectEndTime = time.Now().UnixNano() + con_Write_Protect_Nanoseconds
return false
}
// 否则,将写锁数量设置为1,并返回成功;并重置写锁保护结束时间;这样读锁和写锁都可以参与锁的竞争;
this.write = 1
this.writeProtectEndTime = time.Now().UnixNano()
// 记录Stack信息
if if_record_stack_info {
this.prevStack = debug.Stack()
}
return true
}
// 写锁定
// timeout:超时毫秒数,timeout<=0则将会死等
// 返回值:
// 成功或失败
// 如果失败,返回上一次成功加锁时的堆栈信息
// 如果失败,返回当前的堆栈信息
func (this *RWLocker) Lock(timeout int) (successful bool, prevStack string, currStack string) {
timeout = getTimeout(timeout)
// 遍历指定的次数(即指定的超时时间)
for i := 0; i < timeout; i++ {
// 如果锁定成功,则返回成功
if this.lock() {
successful = true
break
}
// 如果锁定失败则休眠1ms然后再重试
time.Sleep(time.Millisecond)
}
// 如果时间结束仍然是失败,则返回上次成功的堆栈信息,以及当前的堆栈信息
if successful == false {
if this.prevStack != nil && len(this.prevStack) > 0 {
prevStack = string(this.prevStack)
}
currStack = string(debug.Stack())
}
return
}
// 写锁定(死等)
func (this *RWLocker) WaitLock() {
successful, prevStack, currStack := this.Lock(0)
if successful == false {
fmt.Printf("RWLocker:WaitLock():{PrevStack:%s, currStack:%s}\n", prevStack, currStack)
}
}
// 释放写锁
func (this *RWLocker) Unlock() {
this.mutex.Lock()
defer this.mutex.Unlock()
this.write = 0
//释放写锁保护时间,避免调时间导致读锁超时
this.writeProtectEndTime = 0
}
// 尝试加读锁
// 返回值:加读锁是否成功
func (this *RWLocker) rlock() bool {
this.mutex.Lock()
defer this.mutex.Unlock()
// 如果已经被锁定,或者处于写锁保护时间段内,则返回失败
if this.write == 1 || time.Now().UnixNano() < this.writeProtectEndTime {
return false
}
// 否则,将读锁数量加1,并返回成功
this.read += 1
// 记录Stack信息
if if_record_stack_info {
this.prevStack = debug.Stack()
}
return true
}
// 读锁定
// timeout:超时毫秒数,timeout<=0则将会死等
// 返回值:
// 成功或失败
// 如果失败,返回上一次成功加锁时的堆栈信息
// 如果失败,返回当前的堆栈信息
func (this *RWLocker) RLock(timeout int) (successful bool, prevStack string, currStack string) {
timeout = getTimeout(timeout)
// 遍历指定的次数(即指定的超时时间)
// 读锁比写锁优先级更低所以每次休眠2ms所以尝试的次数就是时间/2
for i := 0; i < timeout; i++ {
// 如果锁定成功,则返回成功
if this.rlock() {
successful = true
break
}
// 如果锁定失败则休眠1ms然后再重试
time.Sleep(time.Millisecond)
}
// 如果时间结束仍然是失败,则返回上次成功的堆栈信息,以及当前的堆栈信息
if successful == false {
if this.prevStack != nil && len(this.prevStack) > 0 {
prevStack = string(this.prevStack)
}
currStack = string(debug.Stack())
}
return
}
// 读锁定(死等)
func (this *RWLocker) WaitRLock() {
successful, prevStack, currStack := this.RLock(0)
if successful == false {
fmt.Printf("RWLocker:WaitRLock():{PrevStack:%s, currStack:%s}\n", prevStack, currStack)
}
}
// 释放读锁
func (this *RWLocker) RUnlock() {
this.mutex.Lock()
defer this.mutex.Unlock()
if this.read > 0 {
this.read -= 1
}
}
// 创建新的读写锁对象
func NewRWLocker() *RWLocker {
return &RWLocker{}
}

View File

@@ -0,0 +1,58 @@
package syncUtil
import (
"sync"
)
// 读写锁工具类
type RWLockerUtil struct {
// 锁集合
lockerMap map[string]*RWLocker
// 锁对象
rwMutex sync.RWMutex
}
// 创建新的锁工具类
func NewRWLockerUtil() *RWLockerUtil {
return &RWLockerUtil{
lockerMap: make(map[string]*RWLocker, 8),
}
}
// 获取锁对象
// lockName:锁名
// 返回值:
// RWLocker:读写锁对象
func (this *RWLockerUtil) GetLock(lockName string) *RWLocker {
var rwLockerObj *RWLocker
var exists bool
func() {
this.rwMutex.RLock()
defer this.rwMutex.RUnlock()
rwLockerObj, exists = this.lockerMap[lockName]
}()
if exists {
return rwLockerObj
}
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
rwLockerObj, exists = this.lockerMap[lockName]
if exists == false {
rwLockerObj = NewRWLocker()
this.lockerMap[lockName] = rwLockerObj
}
return rwLockerObj
}
// 释放读写锁对象
// lockName:锁名
func (this *RWLockerUtil) ReleaseLock(lockName string) {
this.rwMutex.Lock()
defer this.rwMutex.Unlock()
delete(this.lockerMap, lockName)
}

View File

@@ -0,0 +1,27 @@
package syncUtil
import (
"fmt"
"testing"
)
var (
rwLockerUtilObj = NewRWLockerUtil()
)
func RWLockerUtil_TestGetLock(t *testing.T) {
count := 100
for i := 1; i <= count; i++ {
rwLockerUtilObj.GetLock(fmt.Sprintf("%d", i))
if lockerCount := len(rwLockerUtilObj.lockerMap); lockerCount != i {
t.Errorf("(GetLock)Expected %d locker, but now got: %d", count, lockerCount)
}
}
for i := count; i > 0; i-- {
rwLockerUtilObj.ReleaseLock(fmt.Sprintf("%d", i))
if lockerCount := len(rwLockerUtilObj.lockerMap); lockerCount != i-1 {
t.Errorf("(ReleaseLock)Expected %d locker, but now got: %d", count, lockerCount)
}
}
}

View File

@@ -0,0 +1,154 @@
package syncUtil
import (
"fmt"
"testing"
"time"
)
func TestRWNewLocker1(t *testing.T) {
count := 1000000
succeedCount := 0
expected := 1000000
goroutineCount := 1
lockerObj := NewRWLocker()
ch := make(chan bool, goroutineCount)
for i := 0; i < goroutineCount; i++ {
go rwLockerTest(lockerObj, &succeedCount, count/goroutineCount, ch)
}
for i := 0; i < goroutineCount; i++ {
<-ch
}
if succeedCount != expected {
t.Errorf("Expected %d, but got %d", expected, succeedCount)
}
}
func TestRWNewLocker2(t *testing.T) {
count := 1000000
succeedCount := 0
expected := 1000000
goroutineCount := 100
lockerObj := NewRWLocker()
ch := make(chan bool, goroutineCount)
for i := 0; i < goroutineCount; i++ {
go rwLockerTest(lockerObj, &succeedCount, count/goroutineCount, ch)
}
for i := 0; i < goroutineCount; i++ {
<-ch
}
if succeedCount != expected {
t.Errorf("Expected %d, but got %d", expected, succeedCount)
}
}
func TestRWNewLocker3(t *testing.T) {
count := 1000000
succeedCount := 0
expected := 1000000
goroutineCount := 10000
lockerObj := NewRWLocker()
ch := make(chan bool, goroutineCount)
for i := 0; i < goroutineCount; i++ {
go rwLockerTest(lockerObj, &succeedCount, count/goroutineCount, ch)
}
for i := 0; i < goroutineCount; i++ {
<-ch
}
if succeedCount != expected {
t.Errorf("Expected %d, but got %d", expected, succeedCount)
}
}
func TestRWNewLocker4(t *testing.T) {
lockerObj := NewRWLocker()
if successful, _, _ := lockerObj.RLock(100); successful == false {
t.Errorf("It should be successful to get a read lock, but now it fails.")
return
}
if successful, _, _ := lockerObj.RLock(100); successful == false {
t.Errorf("It should be successful to get a read lock, but now it fails.")
return
}
if successful, _, _ := lockerObj.RLock(100); successful == false {
t.Errorf("It should be successful to get a read lock, but now it fails.")
return
}
lockerObj.RUnlock()
lockerObj.RUnlock()
lockerObj.RUnlock()
if successful, _, _ := lockerObj.Lock(100); successful == false {
t.Errorf("It should be successful to get a write lock, but now it fails.")
return
}
if successful, _, _ := lockerObj.Lock(100); successful {
t.Errorf("It should be failed to get a write lock, but now it succeeds.")
return
}
if successful, _, _ := lockerObj.RLock(100); successful {
t.Errorf("It should be failed to get a read lock, but now it succeeds.")
return
}
lockerObj.Unlock()
}
func TestRWNewLocker5(t *testing.T) {
count := 100
rwLockerObj := NewRWLocker()
ch := make(chan bool, 100)
for i := 0; i < count; i++ {
go func(num int, ch chan bool) {
if num%2 == 0 {
if successful, _, _ := rwLockerObj.Lock(100); successful {
fmt.Println("I get write lock.")
time.Sleep(2 * time.Millisecond)
rwLockerObj.Unlock()
} else {
fmt.Println("Write lock timeout")
}
} else {
if successful, _, _ := rwLockerObj.RLock(100); successful {
fmt.Println("I get read lock.")
time.Sleep(2 * time.Millisecond)
rwLockerObj.RUnlock()
} else {
fmt.Println("Read lock timeout")
}
}
ch <- true
}(i, ch)
}
for i := 0; i < count; i++ {
<-ch
}
}
func rwLockerTest(lockerObj *RWLocker, succeedCount *int, count int, ch chan bool) {
if success, _, _ := lockerObj.Lock(10000); !success {
fmt.Printf("[%v]获取锁超时\n", time.Now())
return
}
defer lockerObj.Unlock()
for i := 0; i < count; i++ {
*succeedCount += 1
}
ch <- true
}

View File

@@ -0,0 +1,33 @@
/*
由于Go不提供超时锁所以自己实现了支持超时机制的互斥锁Locker和读写锁RWLocker。
为了方便供第三方程序使用提供了根据Key获取超时互斥锁和超时读写锁的复合对象LockerUtil和RWLockerUtil。
为了在出现锁超时时方便查找问题,会记录上次成功获得锁时的堆栈信息;并且在本次获取锁失败时,同时返回上次成功时的堆栈信息和本次的堆栈信息。
*/
package syncUtil
const (
// 默认超时的毫秒数(1小时)
con_Default_Timeout_Milliseconds = 60 * 60 * 1000
// 写锁保护时间(纳秒)
con_Write_Protect_Nanoseconds = 5 * 1000 * 1000
)
var (
// 是否记录堆栈信息的状态
if_record_stack_info = false
)
// 获取超时时间
func getTimeout(timeout int) int {
if timeout > 0 {
return timeout
} else {
return con_Default_Timeout_Milliseconds
}
}
// 设置是否记录堆栈信息的状态
func SetIfRecordStackInfo(value bool) {
if_record_stack_info = value
}