golang实现读写锁TryLock功能的插件库go-trylock的使用

go-trylock - Golang实现读写锁TryLock功能的插件库

介绍

go-trylock 是一个为Golang提供TryLock功能的读写锁插件库,它在标准库sync.RWMutex的基础上增加了TryLock相关功能。

接口

go-trylock 实现了sync.Locker接口,并提供了与sync.RWMutex相同的接口。

使用示例

下面是一个完整的示例,展示如何使用go-trylock的各种功能:

package main

import (
	"context"
	"errors"
	"log"
	"time"
	
	"github.com/subchen/go-trylock/v2"
)

var mu = trylock.New()

func main() {
	// 示例1: 普通写锁
	if err := goroutineWrite(); err != nil {
		log.Println(err)
	}

	// 示例2: 带超时的写锁
	if err := goroutineWriteTimeout(); err != nil {
		log.Println(err)
	}

	// 示例3: 普通读锁
	if err := goroutineRead(); err != nil {
		log.Println(err)
	}

	// 示例4: 带超时的读锁
	if err := goroutineReadTimeout(); err != nil {
		log.Println(err)
	}
}

// 尝试获取写锁(无超时)
func goroutineWrite() error {
	if ok := mu.TryLock(context.Background()); !ok {
		return errors.New("timeout, cannot TryLock !!!")
	}
	defer mu.Unlock()
	
	// 写入操作
	log.Println("Write operation started")
	time.Sleep(500 * time.Millisecond)
	log.Println("Write operation completed")
	
	return nil
}

// 尝试获取写锁(带超时)
func goroutineWriteTimeout() error {
	if ok := mu.TryLockTimeout(1 * time.Second); !ok {
		return errors.New("timeout, cannot TryLock !!!")
	}
	defer mu.Unlock()
	
	// 写入操作
	log.Println("Write operation with timeout started")
	time.Sleep(500 * time.Millisecond)
	log.Println("Write operation with timeout completed")
	
	return nil
}

// 尝试获取读锁(无超时)
func goroutineRead() error {
	if ok := mu.RTryLock(context.Background()); !ok {
		return errors.New("timeout, cannot RTryLock !!!")
	}
	defer mu.RUnlock()
	
	// 读取操作
	log.Println("Read operation started")
	time.Sleep(500 * time.Millisecond)
	log.Println("Read operation completed")
	
	return nil
}

// 尝试获取读锁(带超时)
func goroutineReadTimeout() error {
	if ok := mu.RTryLockTimeout(1 * time.Second); !ok {
		return errors.New("timeout, cannot RTryLock !!!")
	}
	defer mu.RUnlock()
	
	// 读取操作
	log.Println("Read operation with timeout started")
	time.Sleep(500 * time.Millisecond)
	log.Println("Read operation with timeout completed")
	
	return nil
}

主要功能

  1. TryLock(context.Context) bool - 尝试获取写锁,可被context取消
  2. TryLockTimeout(time.Duration) bool - 带超时的尝试获取写锁
  3. RTryLock(context.Context) bool - 尝试获取读锁,可被context取消
  4. RTryLockTimeout(time.Duration) bool - 带超时的尝试获取读锁

许可证

Apache 2.0


更多关于golang实现读写锁TryLock功能的插件库go-trylock的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现读写锁TryLock功能的插件库go-trylock的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


go-trylock: Golang 读写锁 TryLock 功能实现

go-trylock 是一个为 Golang 标准库 sync.RWMutex 提供 TryLock 功能的扩展库,允许非阻塞地尝试获取锁。

为什么需要 TryLock

标准库的 sync.RWMutex 提供了 Lock()RLock() 方法,但它们都是阻塞式的。有时候我们需要:

  • 尝试获取锁,如果失败立即返回而不是阻塞
  • 实现超时获取锁的逻辑
  • 避免死锁情况

安装

go get github.com/desertbit/go-trylock

基本用法

1. 互斥锁 TryLock

package main

import (
	"fmt"
	"time"
	
	"github.com/desertbit/go-trylock"
)

func main() {
	var mu trylock.Mutex

	// 第一个协程获取锁
	go func() {
		mu.Lock()
		fmt.Println("Goroutine 1: Lock acquired")
		time.Sleep(2 * time.Second)
		mu.Unlock()
		fmt.Println("Goroutine 1: Lock released")
	}()

	// 主协程尝试获取锁
	time.Sleep(500 * time.Millisecond) // 等待第一个协程获取锁
	
	if mu.TryLock() {
		fmt.Println("Main: Lock acquired")
		mu.Unlock()
	} else {
		fmt.Println("Main: Failed to acquire lock")
	}

	time.Sleep(3 * time.Second) // 等待所有协程完成
}

2. 读写锁 TryRLock

package main

import (
	"fmt"
	"time"
	
	"github.com/desertbit/go-trylock"
)

func main() {
	var rw trylock.RWMutex

	// 写锁协程
	go func() {
		rw.Lock()
		fmt.Println("Writer: Write lock acquired")
		time.Sleep(2 * time.Second)
		rw.Unlock()
		fmt.Println("Writer: Write lock released")
	}()

	// 读锁尝试协程
	go func() {
		time.Sleep(500 * time.Millisecond)
		if rw.TryRLock() {
			fmt.Println("Reader 1: Read lock acquired")
			time.Sleep(1 * time.Second)
			rw.RUnlock()
			fmt.Println("Reader 1: Read lock released")
		} else {
			fmt.Println("Reader 1: Failed to acquire read lock")
		}
	}()

	// 另一个读锁尝试协程
	go func() {
		time.Sleep(1 * time.Second)
		if rw.TryRLock() {
			fmt.Println("Reader 2: Read lock acquired")
			time.Sleep(1 * time.Second)
			rw.RUnlock()
			fmt.Println("Reader 2: Read lock released")
		} else {
			fmt.Println("Reader 2: Failed to acquire read lock")
		}
	}()

	time.Sleep(4 * time.Second) // 等待所有协程完成
}

高级功能

带超时的 TryLock

package main

import (
	"fmt"
	"time"
	
	"github.com/desertbit/go-trylock"
)

func main() {
	var mu trylock.Mutex

	// 先让一个协程持有锁
	go func() {
		mu.Lock()
		fmt.Println("Goroutine: Lock acquired")
		time.Sleep(3 * time.Second)
		mu.Unlock()
		fmt.Println("Goroutine: Lock released")
	}()

	// 主协程尝试带超时获取锁
	time.Sleep(500 * time.Millisecond) // 确保第一个协程先获取锁
	
	fmt.Println("Main: Trying to acquire lock with timeout...")
	if mu.TryLockTimeout(1 * time.Second) {
		fmt.Println("Main: Lock acquired within timeout")
		mu.Unlock()
	} else {
		fmt.Println("Main: Failed to acquire lock within timeout")
	}

	time.Sleep(4 * time.Second)
}

实现原理

go-trylock 的实现主要基于:

  1. 使用 sync/atomic 包实现无锁状态检测
  2. 通过 CAS (Compare-And-Swap) 操作尝试获取锁
  3. 对于超时功能,使用 time.After 和 select 实现

注意事项

  1. TryLock 不是万能的,过度使用可能导致活锁问题
  2. 在大多数情况下,阻塞式锁是更好的选择
  3. 使用 TryLock 时要特别注意锁的释放,避免泄露

性能考虑

TryLock 操作通常比阻塞式锁更快,因为它不需要进入等待队列。但在高竞争场景下,频繁的 TryLock 尝试可能导致 CPU 使用率升高。

替代方案

如果你需要更复杂的锁功能,也可以考虑:

  • github.com/bsm/redis-lock - 基于 Redis 的分布式锁
  • github.com/ulule/limiter - 限流器,可用于控制并发

go-trylock 是一个轻量级的解决方案,适合需要在标准锁基础上添加非阻塞功能的场景。

回到顶部