2 Commits
Author SHA1 Message Date
xiaoqidun 60204bcee6 fix(修复错误): 修复锁超时失效问题 2026-08-24 14:41:14 +08:00
xiaoqidun 5b5bca65b0 feat(更新版本): module调整为1.24.0 2026-01-21 23:18:54 +08:00
6 changed files with 100 additions and 34 deletions

No files matched your search

+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2025 肖其顿 (XIAO QI DUN)
Copyright 2025-2026 肖其顿 (XIAO QI DUN)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,5 +1,5 @@
klock
Copyright 2025 肖其顿 (XIAO QI DUN)
Copyright 2025-2026 肖其顿 (XIAO QI DUN)
This product includes software developed by
肖其顿 (XIAO QI DUN) (https://github.com/xiaoqidun/klock).
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2025 肖其顿 (XIAO QI DUN)
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,3 +1,3 @@
module github.com/xiaoqidun/klock
go 1.25.1
go 1.24.0
+33 -15
View File
@@ -1,4 +1,4 @@
// Copyright 2025 肖其顿 (XIAO QI DUN)
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -185,6 +185,7 @@ func (kl *KeyLock) TryLock(key string) bool {
// LockWithTimeout 尝试在给定的时间内为指定的键获取写锁。
// 如果在超时前成功获取锁,则返回 true;否则返回 false。
func (kl *KeyLock) LockWithTimeout(key string, timeout time.Duration) bool {
deadline := time.Now().Add(timeout)
le := kl.prepareLock(key)
// 立即尝试一次,以避免在锁可用时产生不必要的延迟。
if le.rw.TryLock() {
@@ -197,17 +198,26 @@ func (kl *KeyLock) LockWithTimeout(key string, timeout time.Duration) bool {
kl.cancelLock(key, le)
}
}()
timer := time.NewTimer(timeout)
defer timer.Stop()
pollInterval := kl.config.InitialPollInterval
maxPollInterval := kl.config.MaxPollInterval
for {
select {
case <-timer.C:
remaining := time.Until(deadline)
if remaining <= 0 {
return false
default:
time.Sleep(pollInterval)
}
sleepInterval := pollInterval
if sleepInterval > remaining {
sleepInterval = remaining
}
time.Sleep(sleepInterval)
if time.Until(deadline) <= 0 {
return false
}
if le.rw.TryLock() {
if time.Until(deadline) <= 0 {
le.rw.Unlock()
return false
}
kl.commitLock(key, le)
acquired = true
return true
@@ -217,7 +227,6 @@ func (kl *KeyLock) LockWithTimeout(key string, timeout time.Duration) bool {
pollInterval = maxPollInterval
}
}
}
}
// RLock 为指定的键获取一个读锁。
@@ -244,6 +253,7 @@ func (kl *KeyLock) TryRLock(key string) bool {
// RLockWithTimeout 尝试在给定的时间内为指定的键获取读锁。
// 如果在超时前成功获取锁,则返回 true;否则返回 false。
func (kl *KeyLock) RLockWithTimeout(key string, timeout time.Duration) bool {
deadline := time.Now().Add(timeout)
le := kl.prepareLock(key)
// 立即尝试一次
if le.rw.TryRLock() {
@@ -256,17 +266,26 @@ func (kl *KeyLock) RLockWithTimeout(key string, timeout time.Duration) bool {
kl.cancelLock(key, le)
}
}()
timer := time.NewTimer(timeout)
defer timer.Stop()
pollInterval := kl.config.InitialPollInterval
maxPollInterval := kl.config.MaxPollInterval
for {
select {
case <-timer.C:
remaining := time.Until(deadline)
if remaining <= 0 {
return false
default:
time.Sleep(pollInterval)
}
sleepInterval := pollInterval
if sleepInterval > remaining {
sleepInterval = remaining
}
time.Sleep(sleepInterval)
if time.Until(deadline) <= 0 {
return false
}
if le.rw.TryRLock() {
if time.Until(deadline) <= 0 {
le.rw.RUnlock()
return false
}
kl.commitLock(key, le)
acquired = true
return true
@@ -276,7 +295,6 @@ func (kl *KeyLock) RLockWithTimeout(key string, timeout time.Duration) bool {
pollInterval = maxPollInterval
}
}
}
}
// Unlock 释放指定键的写锁。
+49 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2025 肖其顿 (XIAO QI DUN)
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -250,6 +250,30 @@ func TestLockWithTimeout(t *testing.T) {
}
}
// TestLockWithTimeoutDoesNotAcquireAfterDeadline 测试写锁在超时后被释放时不会再被获取。
func TestLockWithTimeoutDoesNotAcquireAfterDeadline(t *testing.T) {
kl := NewWithConfig(Config{
MaxPollInterval: 50 * time.Millisecond,
InitialPollInterval: 50 * time.Millisecond,
})
key := "test_key"
released := make(chan struct{})
kl.Lock(key)
go func() {
time.Sleep(20 * time.Millisecond)
kl.Unlock(key)
close(released)
}()
acquired := kl.LockWithTimeout(key, 10*time.Millisecond)
if acquired {
kl.Unlock(key)
}
<-released
if acquired {
t.Fatal("LockWithTimeout 不应该在超时后获取锁")
}
}
// TestRLockWithTimeout 测试带超时的读锁获取功能。
func TestRLockWithTimeout(t *testing.T) {
kl := New()
@@ -270,6 +294,30 @@ func TestRLockWithTimeout(t *testing.T) {
}
}
// TestRLockWithTimeoutDoesNotAcquireAfterDeadline 测试读锁在超时后被释放时不会再被获取。
func TestRLockWithTimeoutDoesNotAcquireAfterDeadline(t *testing.T) {
kl := NewWithConfig(Config{
MaxPollInterval: 50 * time.Millisecond,
InitialPollInterval: 50 * time.Millisecond,
})
key := "test_key"
released := make(chan struct{})
kl.Lock(key)
go func() {
time.Sleep(20 * time.Millisecond)
kl.Unlock(key)
close(released)
}()
acquired := kl.RLockWithTimeout(key, 10*time.Millisecond)
if acquired {
kl.RUnlock(key)
}
<-released
if acquired {
t.Fatal("RLockWithTimeout 不应该在超时后获取锁")
}
}
// TestPanicOnUnlockOfUnlockedKey 测试对未锁定的键执行 Unlock 操作是否会引发 panic。
func TestPanicOnUnlockOfUnlockedKey(t *testing.T) {
defer func() {