代码拉取完成,页面将自动刷新
// Package singletonUtil 提供单实例运行检测功能
// 使用文件锁机制确保同一时间只有一个程序实例在运行
package singletonUtil
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"log/slog"
"os"
"path/filepath"
"github.com/gofrs/flock"
)
// instanceLock flock 锁句柄,需要在程序退出时释放
var instanceLock *flock.Flock
// IsSingleton 检查当前程序是否只有一个实例在运行,使用文件锁机制
// 返回值:
//
// true - 当前程序是唯一运行的实例
// false - 检测到其他相同程序实例正在运行,或无法获取文件锁
//
// 机制说明:
// - 在系统临时目录创建基于程序路径的锁文件
// - 使用 github.com/gofrs/flock 库实现跨平台文件锁
// - 当程序正常退出时,锁会自动释放
//
// 使用示例:
//
// // 在 main 函数开头调用
// defer SingletonUtil.ReleaseInstanceLock()
// if !SingletonUtil.IsSingleton() {
// log.Fatal("程序已在运行")
// }
func IsSingleton() bool {
// 获取可执行文件路径
exePath, err := os.Executable()
if err != nil {
slog.Error("获取可执行文件路径失败", "error", err)
return false
}
// 解析符号链接获取真实路径
exePath, err = filepath.EvalSymlinks(exePath)
if err != nil {
slog.Error("解析可执行文件路径失败", "error", err)
return false
}
// 计算程序路径的哈希值作为锁文件名的一部分,避免路径过长
hasher := sha256.New()
hasher.Write([]byte(exePath))
pathHash := hex.EncodeToString(hasher.Sum(nil))[:16]
// 创建锁文件路径
tempDir := os.TempDir()
lockFilePath := filepath.Join(tempDir, fmt.Sprintf(".%s.lock", pathHash))
// 使用 flock 库创建锁文件
fileLock := flock.New(lockFilePath)
// 尝试获取排他锁(非阻塞模式)
locked, err := fileLock.TryLock()
if err != nil {
slog.Error("创建文件锁失败", "path", lockFilePath, "error", err)
return false
}
if !locked {
slog.Info("检测到程序已在运行", "lock_file", lockFilePath)
return false
}
// 将锁文件句柄保存到全局变量,确保程序退出时锁会释放
instanceLock = fileLock
slog.Debug("成功获取文件锁", "lock_file", lockFilePath)
return true
}
// ReleaseInstanceLock 释放实例锁(在程序退出时调用)
// 通常在 main 函数开头使用 defer 调用:
//
// defer SingletonUtil.ReleaseInstanceLock()
func ReleaseInstanceLock() {
if instanceLock != nil {
// Unlock 会释放锁并删除锁文件
if err := instanceLock.Unlock(); err != nil {
slog.Warn("释放文件锁失败", "error", err)
} else {
slog.Debug("已释放文件锁")
}
instanceLock = nil
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。