diff --git a/concurrency/channel_example_test.go b/concurrency/channel_example_test.go index 90bd2ec..2f0d2f5 100644 --- a/concurrency/channel_example_test.go +++ b/concurrency/channel_example_test.go @@ -219,7 +219,6 @@ func ExampleKeyedLocker_Do() { fmt.Println("Task successfully executed.") } - // 再次尝试获取同一把锁,任务会被阻塞,直到释放锁 ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second) defer cancel2() diff --git a/docs/api/packages/concurrency.md b/docs/api/packages/concurrency.md index d893b27..8444090 100644 --- a/docs/api/packages/concurrency.md +++ b/docs/api/packages/concurrency.md @@ -7,6 +7,7 @@ ## 源码: - [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go) +- [https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go](https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go)
@@ -35,6 +36,18 @@ import ( - [Take](#Take) - [Tee](#Tee) +### KeyedLocker + +- [NewKeyedLocker](#NewKeyedLocker) +- [KeyedLocker_Do](#Do) +- [NewRWKeyedLocker](#NewRWKeyedLocker) +- [RWKeyedLocker_RLock](#RLock) +- [RWKeyedLocker_Lock](#Lock) +- [NewTryKeyedLocker](#NewTryKeyedLocker) +- [TryKeyedLocker_TryLock](#TryLock) +- [TryKeyedLocker_Unlock](#Unlock) + + ## 文档 @@ -452,3 +465,385 @@ func main() { // 1 } ``` + +### KeyedLocker + +### NewKeyedLocker + +NewKeyedLocker创建一个新的KeyedLocker,并为锁的过期设置指定的 TTL。KeyedLocker 是一个简单的键值锁实现,允许非阻塞的锁获取。
+ +函数签名: + +```go +func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K] +``` + +示例:[运行](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewKeyedLocker[string](2 * time.Second) + + task := func() { + fmt.Println("Executing task...") + time.Sleep(1 * time.Second) + fmt.Println("Task completed.") + } + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + if err := locker.Do(ctx, "mykey", task); err != nil { + log.Fatalf("Error executing task: %v\n", err) + } else { + fmt.Println("Task successfully executed.") + } + + ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel2() + + if err := locker.Do(ctx2, "mykey", task); err != nil { + log.Fatalf("Error executing task: %v\n", err) + } else { + fmt.Println("Task successfully executed.") + } + + // Output: + // Executing task... + // Task completed. + // Task successfully executed. + // Executing task... + // Task completed. + // Task successfully executed. +} +``` + +### KeyedLocker_Do + +为指定的键获取锁并执行提供的函数。
+ +函数签名: + +```go +func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error +``` + +示例:[运行](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewKeyedLocker[string](2 * time.Second) + + task := func() { + fmt.Println("Executing task...") + time.Sleep(1 * time.Second) + fmt.Println("Task completed.") + } + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + if err := locker.Do(ctx, "mykey", task); err != nil { + log.Fatalf("Error executing task: %v\n", err) + } else { + fmt.Println("Task successfully executed.") + } + + ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel2() + + if err := locker.Do(ctx2, "mykey", task); err != nil { + log.Fatalf("Error executing task: %v\n", err) + } else { + fmt.Println("Task successfully executed.") + } + + // Output: + // Executing task... + // Task completed. + // Task successfully executed. + // Executing task... + // Task completed. + // Task successfully executed. +} +``` + +### NewRWKeyedLocker + +NewRWKeyedLocker创建一个新的RWKeyedLocker,并为锁的过期设置指定的 TTL。RWKeyedLocker 是一个简单的键值读写锁实现,允许非阻塞的锁获取。
+ +函数签名: + +```go +func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K] +``` +示例:[运行](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewRWKeyedLocker[string](2 * time.Second) + + // Simulate a key + key := "resource_key" + + fn := func() { + fmt.Println("Starting write operation...") + // Simulate write operation, assuming it takes 2 seconds + time.Sleep(200 * time.Millisecond) + fmt.Println("Write operation completed!") + } + + // Acquire the write lock and execute the operation + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + // Execute the lock operation with a 3-second timeout + err := locker.Lock(ctx, key, fn) + if err != nil { + return + } + + //output: + //Starting write operation... + //Write operation completed! +} +``` + +### RWKeyedLocker_RLock + +RLock为指定的键获取读锁并执行提供的函数。
+ +函数签名: + +```go +func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error +``` +示例:[运行](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewRWKeyedLocker[string](2 * time.Second) + + // Simulate a key + key := "resource_key" + + fn := func() { + fmt.Println("Starting write operation...") + // Simulate write operation, assuming it takes 2 seconds + time.Sleep(200 * time.Millisecond) + fmt.Println("Write operation completed!") + } + + // Acquire the write lock and execute the operation + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + // Execute the lock operation with a 3-second timeout + err := locker.RLock(ctx, key, fn) + if err != nil { + return + } + + //output: + //Starting write operation... + //Write operation completed! +} +``` + +### RWKeyedLocker_Lock + +RLock为指定的键获取锁并执行提供的函数。
+ +函数签名: + +```go +func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error +``` +示例:[运行](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := NewRWKeyedLocker[string](2 * time.Second) + + // Simulate a key + key := "resource_key" + + fn := func() { + fmt.Println("Starting write operation...") + // Simulate write operation, assuming it takes 2 seconds + time.Sleep(200 * time.Millisecond) + fmt.Println("Write operation completed!") + } + + // Acquire the write lock and execute the operation + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + // Execute the lock operation with a 3-second timeout + err := locker.Lock(ctx, key, fn) + if err != nil { + return + } + + //output: + //Starting write operation... + //Write operation completed! +} +``` + +### NewTryKeyedLocker + +创建一个TryKeyedLocker实例,TryKeyedLocker是KeyedLocker的非阻塞版本。
+ +函数签名: + +```go +func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K] +``` +示例:[运行](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewTryKeyedLocker[string]() + + key := "resource_key" + + if locker.TryLock(key) { + fmt.Println("Lock acquired") + time.Sleep(1 * time.Second) + // Unlock after work is done + locker.Unlock(key) + fmt.Println("Lock released") + } else { + fmt.Println("Lock failed") + } + + //output: + //Lock acquired + //Lock released +} +``` + +### TryKeyedLocker_TryLock + +TryLock尝试获取指定键的锁。如果锁成功获取,则返回true,否则返回false。
+ +函数签名: + +```go +func (l *TryKeyedLocker[K]) TryLock(key K) bool +``` + +示例:[运行](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewTryKeyedLocker[string]() + + key := "resource_key" + + if locker.TryLock(key) { + fmt.Println("Lock acquired") + time.Sleep(1 * time.Second) + // Unlock after work is done + locker.Unlock(key) + fmt.Println("Lock released") + } else { + fmt.Println("Lock failed") + } + + //output: + //Lock acquired + //Lock released +} +``` + +### TryKeyedLocker_Unlock + +释放指定键的锁。
+ +函数签名: + +```go +func (l *TryKeyedLocker[K]) Unlock(key K) +``` + +示例:[运行](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewTryKeyedLocker[string]() + + key := "resource_key" + + if locker.TryLock(key) { + fmt.Println("Lock acquired") + time.Sleep(1 * time.Second) + // Unlock after work is done + locker.Unlock(key) + fmt.Println("Lock released") + } else { + fmt.Println("Lock failed") + } + + //output: + //Lock acquired + //Lock released +} +``` \ No newline at end of file diff --git a/docs/en/api/packages/concurrency.md b/docs/en/api/packages/concurrency.md index 04340a2..0d2b66c 100644 --- a/docs/en/api/packages/concurrency.md +++ b/docs/en/api/packages/concurrency.md @@ -6,6 +6,7 @@ Package concurrency contain some functions to support concurrent programming. eg ## Source: - [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go) +- [https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go](https://github.com/duke-git/lancet/blob/main/concurrency/keyed_locker.go) @@ -19,7 +20,9 @@ import ( ## Index + ### Channel + - [NewChannel](#NewChannel) - [Bridge](#Bridge) - [FanIn](#FanIn) @@ -31,6 +34,17 @@ import ( - [Take](#Take) - [Tee](#Tee) +### KeyedLocker + +- [NewKeyedLocker](#NewKeyedLocker) +- [KeyedLocker_Do](#Do) +- [NewRWKeyedLocker](#NewRWKeyedLocker) +- [RWKeyedLocker_RLock](#RLock) +- [RWKeyedLocker_Lock](#Lock) +- [NewTryKeyedLocker](#NewTryKeyedLocker) +- [TryKeyedLocker_TryLock](#TryLock) +- [TryKeyedLocker_Unlock](#Unlock) + ## Documentation @@ -437,4 +451,386 @@ func main() { // 1 // 1 } +``` + +### KeyedLocker + +### NewKeyedLocker + +KeyedLocker is a simple implementation of a keyed locker that allows for non-blocking lock acquisition.
+ +Signature: + +```go +func NewKeyedLocker[K comparable](ttl time.Duration) *KeyedLocker[K] +``` + +Example:[Run](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewKeyedLocker[string](2 * time.Second) + + task := func() { + fmt.Println("Executing task...") + time.Sleep(1 * time.Second) + fmt.Println("Task completed.") + } + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + if err := locker.Do(ctx, "mykey", task); err != nil { + log.Fatalf("Error executing task: %v\n", err) + } else { + fmt.Println("Task successfully executed.") + } + + ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel2() + + if err := locker.Do(ctx2, "mykey", task); err != nil { + log.Fatalf("Error executing task: %v\n", err) + } else { + fmt.Println("Task successfully executed.") + } + + // Output: + // Executing task... + // Task completed. + // Task successfully executed. + // Executing task... + // Task completed. + // Task successfully executed. +} +``` + +### KeyedLocker_Do + +Acquires a lock for the specified key and executes the provided function.
+ +Signature: + +```go +func (l *KeyedLocker[K]) Do(ctx context.Context, key K, fn func()) error +``` + +Example:[Run](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewKeyedLocker[string](2 * time.Second) + + task := func() { + fmt.Println("Executing task...") + time.Sleep(1 * time.Second) + fmt.Println("Task completed.") + } + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + if err := locker.Do(ctx, "mykey", task); err != nil { + log.Fatalf("Error executing task: %v\n", err) + } else { + fmt.Println("Task successfully executed.") + } + + ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel2() + + if err := locker.Do(ctx2, "mykey", task); err != nil { + log.Fatalf("Error executing task: %v\n", err) + } else { + fmt.Println("Task successfully executed.") + } + + // Output: + // Executing task... + // Task completed. + // Task successfully executed. + // Executing task... + // Task completed. + // Task successfully executed. +} +``` + +### NewRWKeyedLocker + +RWKeyedLocker is a read-write version of KeyedLocker.
+ +Signature: + +```go +func NewRWKeyedLocker[K comparable](ttl time.Duration) *RWKeyedLocker[K] +``` +Example:[Run](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewRWKeyedLocker[string](2 * time.Second) + + // Simulate a key + key := "resource_key" + + fn := func() { + fmt.Println("Starting write operation...") + // Simulate write operation, assuming it takes 2 seconds + time.Sleep(200 * time.Millisecond) + fmt.Println("Write operation completed!") + } + + // Acquire the write lock and execute the operation + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + // Execute the lock operation with a 3-second timeout + err := locker.Lock(ctx, key, fn) + if err != nil { + return + } + + //output: + //Starting write operation... + //Write operation completed! +} +``` + +### RWKeyedLocker_RLock + +Acquires a read lock for the specified key and executes the provided function.
+ +Signature: + +```go +func (l *RWKeyedLocker[K]) RLock(ctx context.Context, key K, fn func()) error +``` +Example:[Run](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewRWKeyedLocker[string](2 * time.Second) + + // Simulate a key + key := "resource_key" + + fn := func() { + fmt.Println("Starting write operation...") + // Simulate write operation, assuming it takes 2 seconds + time.Sleep(200 * time.Millisecond) + fmt.Println("Write operation completed!") + } + + // Acquire the write lock and execute the operation + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + // Execute the lock operation with a 3-second timeout + err := locker.RLock(ctx, key, fn) + if err != nil { + return + } + + //output: + //Starting write operation... + //Write operation completed! +} +``` + +### RWKeyedLocker_Lock + +Acquires a write lock for the specified key and executes the provided function.
+ +Signature: + +```go +func (l *RWKeyedLocker[K]) Lock(ctx context.Context, key K, fn func()) error +``` +Example:[Run](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := NewRWKeyedLocker[string](2 * time.Second) + + // Simulate a key + key := "resource_key" + + fn := func() { + fmt.Println("Starting write operation...") + // Simulate write operation, assuming it takes 2 seconds + time.Sleep(200 * time.Millisecond) + fmt.Println("Write operation completed!") + } + + // Acquire the write lock and execute the operation + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + // Execute the lock operation with a 3-second timeout + err := locker.Lock(ctx, key, fn) + if err != nil { + return + } + + //output: + //Starting write operation... + //Write operation completed! +} +``` + +### NewTryKeyedLocker + +TryKeyedLocker is a non-blocking version of KeyedLocker.
+ +Signature: + +```go +func NewTryKeyedLocker[K comparable]() *TryKeyedLocker[K] +``` +Example:[Run](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewTryKeyedLocker[string]() + + key := "resource_key" + + if locker.TryLock(key) { + fmt.Println("Lock acquired") + time.Sleep(1 * time.Second) + // Unlock after work is done + locker.Unlock(key) + fmt.Println("Lock released") + } else { + fmt.Println("Lock failed") + } + + //output: + //Lock acquired + //Lock released +} +``` + +### TryKeyedLocker_TryLock + +TryLock tries to acquire a lock for the specified key.
+ +Signature: + +```go +func (l *TryKeyedLocker[K]) TryLock(key K) bool +``` + +Example:[Run](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewTryKeyedLocker[string]() + + key := "resource_key" + + if locker.TryLock(key) { + fmt.Println("Lock acquired") + time.Sleep(1 * time.Second) + // Unlock after work is done + locker.Unlock(key) + fmt.Println("Lock released") + } else { + fmt.Println("Lock failed") + } + + //output: + //Lock acquired + //Lock released +} +``` + +### TryKeyedLocker_Unlock + +Unlock releases the lock for the specified key.
+ +Signature: + +```go +func (l *TryKeyedLocker[K]) Unlock(key K) +``` + +Example:[Run](https://go.dev/play/p/todo) + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/concurrency" +) + +func main() { + locker := concurrency.NewTryKeyedLocker[string]() + + key := "resource_key" + + if locker.TryLock(key) { + fmt.Println("Lock acquired") + time.Sleep(1 * time.Second) + // Unlock after work is done + locker.Unlock(key) + fmt.Println("Lock released") + } else { + fmt.Println("Lock failed") + } + + //output: + //Lock acquired + //Lock released +} ``` \ No newline at end of file