1.创建一个Go程序来实现基于Redis的互斥锁。这个程序将尝试获取一个锁,成功后对文件进行操作,并且在完成操作后释放锁
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"os"
"time"
)
var ctx = context.Background()
func main() {
// 创建Redis客户端
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis服务器地址
Password: "", // 密码,没有则留空
DB: 0, // 使用默认DB
})
lockKey := "file_lock"
lockValue := "1"
lockTimeout := 10 * time.Second
// 尝试获取锁
success, err := rdb.SetNX(ctx, lockKey, lockValue, lockTimeout).Result()
if err != nil || !success {
fmt.Println("Failed to acquire the lock.")
return
}
fmt.Println("Lock acquired, processing the file...")
// 模拟文件操作
processFile()
// 释放锁
_, err = rdb.Del(ctx, lockKey).Result()
if err != nil {
fmt.Println("Failed to release the lock.")
return
}
fmt.Println("Lock released.")
}
func processFile() {
// 模拟对文件的操作,这里只是简单地等待一段时间
time.Sleep(5 * time.Second)
fmt.Println("File processed.")
}
解决的问题:
1.锁的自动过期:为了防止进程在持有锁的状态下异常终止而导致其他进程永远无法获取锁,避免死锁的产生
2.锁的释放:确保在文件操作完成后正确释放锁是非常重要的,即使在操作过程中遇到错误也应该释放锁,以避免死锁。
引出的问题:
1.假如文件处理的时间不是固定的,超了10s的过期时间,就会被其他的进程把锁给抢了
2.针对上面的问题,改进的代码:
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"os"
"sync"
"time"
)
var ctx = context.Background()
func main() {
// 创建Redis客户端
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis服务器地址
Password: "", // 密码,没有则留空
DB: 0, // 使用默认DB
})
lockKey := "file_lock"
lockValue := "unique_lock_id_" + time.Now().String() // 使用时间戳作为锁的唯一标识
lockTimeout := 10 * time.Second
// 尝试获取锁
success, err := rdb.SetNX(ctx, lockKey, lockValue, lockTimeout).Result()
if err != nil {
fmt.Printf("Error trying to acquire the lock: %v\n", err)
return
}
if !success {
fmt.Println("Lock is already held by another instance.")
return
}
// 使用WaitGroup和context来控制锁续期goroutine的终止
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(ctx)
defer cancel() // 确保在函数退出时取消context
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-ctx.Done():
return
case <-time.After(lockTimeout / 2):
_, err := rdb.Expire(ctx, lockKey, lockTimeout).Result()
if err != nil {
fmt.Printf("Error renewing the lock: %v\n", err)
return
}
}
}
}()
fmt.Println("Lock acquired, processing the file...")
// 模拟文件操作
processFile()
// 检查锁的值,确保只有锁的持有者才能释放锁
val, err := rdb.Get(ctx, lockKey).Result()
if err != nil {
// 锁可能已经过期并被其他实例获取
fmt.Printf("Error getting lock value: %v\n", err)
} else if val == lockValue {
_, err = rdb.Del(ctx, lockKey).Result()
if err != nil {
fmt.Printf("Error releasing the lock: %v\n", err)
}
}
cancel() // 告诉续期goroutine停止
wg.Wait() // 等待续期goroutine完成
fmt.Println("File processed and lock released.")
}
func processFile() {
// 模拟对文件的操作,这里只是简单地等待一段时间
time.Sleep(5 * time.Second)
fmt.Println("File processed.")
}
标签:err,ctx,fmt,redis,互斥,context,time
From: https://blog.csdn.net/m0_64558520/article/details/139687586