本文目录导读:

flock命令(Linux/Unix 推荐)mkdir原子操作(通用所有系统)ln硬链接(避免竞态条件)lockfile命令(需安装 procmail)- PowerShell 中的文件锁(Windows)
- Python 中的文件锁
- 关键注意事项
- 推荐方案
利用操作系统提供的原子操作,确保只有一个进程能成功获取锁,以下是几种常见的方法:
flock 命令(Linux/Unix 推荐)
最常用且可靠的方法,通过文件描述符锁定文件。
#!/bin/bash
LOCK_FILE="/tmp/myapp.lock"
# 打开文件并获取文件描述符
exec 200>"$LOCK_FILE"
# 尝试获取锁(非阻塞)
flock -n 200 || {
echo "另一个实例正在运行,退出"
exit 1
}
# 或者使用阻塞等待
# flock 200 || exit 1
echo "获得锁,开始执行任务..."
sleep 30 # 模拟任务
echo "任务完成,释放锁"
# 脚本结束时自动释放锁(文件描述符关闭时)
关键点:
exec 200>"$LOCK_FILE":打开/创建文件,文件描述符200flock -n:非阻塞尝试(-n),失败立即返回- 锁在文件描述符关闭时自动释放
mkdir 原子操作(通用所有系统)
利用 mkdir 的原子性(要么成功创建,要么失败):
#!/bin/bash
LOCK_DIR="/tmp/myapp.lockdir"
# 尝试创建目录作为锁,mkdir是原子操作
if mkdir "$LOCK_DIR" 2>/dev/null; then
echo "获得锁,开始执行任务..."
trap "rmdir '$LOCK_DIR'; echo '锁已释放'" EXIT
sleep 30 # 模拟任务
else
echo "另一个实例正在运行,退出"
exit 1
fi
优点:跨平台(Linux/Windows/BSD都可使用)
缺点:需要手动清理(使用 trap 确保清理)
ln 硬链接(避免竞态条件)
ln 也是原子操作,比 mkdir 更可靠:
#!/bin/bash
LOCK_FILE="/tmp/myapp.lock"
PID=$$
LOCK_TARGET="/tmp/lock_target_$PID"
# 创建唯一临时文件
touch "$LOCK_TARGET"
# 尝试创建硬链接,如果文件已存在则失败
if ln "$LOCK_TARGET" "$LOCK_FILE" 2>/dev/null; then
echo "获得锁 $LOCK_FILE"
trap "rm -f '$LOCK_FILE' '$LOCK_TARGET'; echo '锁已释放'" EXIT
sleep 30 # 模拟任务
else
echo "另一个实例正在运行"
rm -f "$LOCK_TARGET"
exit 1
fi
lockfile 命令(需安装 procmail)
#!/bin/bash
LOCK_FILE="/tmp/myapp.lock"
# 创建锁文件(等待10秒超时)
lockfile -r -1 -l 3600 "$LOCK_FILE" 2>/dev/null || {
echo "无法获取锁(超时或权限问题)"
exit 1
}
echo "获得锁,开始执行..."
# 函数结束时自动删除锁
trap "rm -f $LOCK_FILE; exit" EXIT
sleep 30
PowerShell 中的文件锁(Windows)
$lockFile = "$env:TEMP\myscript.lock"
$lockStream = $null
try {
# 尝试打开文件,FileShare.None 表示独占访问
$lockStream = [System.IO.File]::Open(
$lockFile,
[System.IO.FileMode]::OpenOrCreate,
[System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::None
)
Write-Host "获得锁,开始执行..."
Start-Sleep -Seconds 30 # 模拟任务
}
catch {
Write-Host "另一个实例正在运行,退出"
exit 1
}
finally {
if ($lockStream -ne $null) {
$lockStream.Close()
Remove-Item $lockFile -Force -ErrorAction SilentlyContinue
}
}
Python 中的文件锁
import fcntl
import sys
import time
LOCK_FILE = "/tmp/myapp.lock"
def main():
# 打开/创建锁文件
with open(LOCK_FILE, 'w') as f:
try:
# fcntl.LOCK_EX: 排他锁, LOCK_NB: 非阻塞
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
print("获得锁,开始执行...")
# 执行任务
time.sleep(30)
print("任务完成,锁自动释放")
except IOError:
print("另一个实例正在运行,退出")
sys.exit(1)
if __name__ == "__main__":
main()
关键注意事项
- 原子操作:选择使用系统提供的原子操作(flock、mkdir、ln)
- 自动清理:使用
trap或finally确保锁文件在异常时也被清理 - 超时机制:防止死锁,如
flock -w 10等待10秒 - PID 验证:可将进程ID写入锁文件,检查进程是否存活:
# 检查锁文件中的PID是否还在运行 if [ -f "$LOCK_FILE" ]; then OLD_PID=$(cat "$LOCK_FILE") if kill -0 "$OLD_PID" 2>/dev/null; then echo "进程 $OLD_PID 还在运行" exit 1 else echo "清除过期锁" rm -f "$LOCK_FILE" fi fi echo $$ > "$LOCK_FILE"
推荐方案
- Linux/Unix 脚本:优先使用
flock方案(最可靠、最简洁) - 跨平台脚本:使用
mkdir方案 - Windows/PowerShell:使用 .NET 的文件流独占访问
- 需要复杂逻辑:使用 Python/Perl 等编程语言实现
选择合适的锁机制取决于你的操作系统和具体需求,关键是理解原子操作和自动释放这两个核心概念。