1. Shell脚本重复执行问题概述
在Linux系统运维工作中,脚本重复执行是一个常见但容易被忽视的问题。想象一下这样的场景:你精心编写了一个数据库备份脚本,设定为每天凌晨2点通过cron定时执行。某天早上你发现数据库异常,检查后发现是备份脚本同时运行了多个实例,导致数据文件损坏。这种情况在实际运维环境中并不罕见,轻则导致资源浪费,重则引发数据一致性问题和系统崩溃。
1.1 重复执行的典型场景
根据我多年的运维经验,脚本重复执行主要发生在以下几种情况:
定时任务重叠:这是最常见的场景。当cron任务执行间隔设置不合理,或者某次执行时间异常延长时,很容易出现前一个实例尚未结束,新的实例又启动的情况。我曾经遇到过这样一个案例:一个日志分析脚本平时运行需要15分钟,但在某天日志量激增时需要运行2小时,而cron设置的是每小时执行一次,结果导致系统内存被耗尽。
手动误操作:在复杂的运维环境中,管理员可能同时打开多个终端窗口,不小心在多个会话中重复执行了同一个脚本。特别是在处理紧急故障时,这种人为失误发生的概率会显著增加。
多用户并发执行:在多人协作的服务器环境中,不同用户可能同时执行相同的管理脚本。比如系统上有多个管理员账户时,两个管理员可能几乎同时执行系统清理脚本,导致清理操作被重复执行。
自动化工具触发:当脚本被配置在CI/CD流水线中,或者被监控系统(如Zabbix、Nagios)调用时,可能因为工具的重试机制或错误配置导致脚本被多次触发。
1.2 重复执行的危害
重复运行的脚本可能造成的影响远比想象中严重:
-
数据损坏:对于涉及文件修改或数据库操作的脚本,并发写入会导致数据不一致。我曾经遇到过一个配置文件被两个脚本实例同时修改,结果文件内容完全混乱的情况。
-
资源耗尽:重复运行的脚本会消耗额外的CPU、内存和IO资源。特别是在处理大数据量时,这种资源消耗可能呈指数级增长。
-
逻辑错误:某些脚本可能设计为只能单实例运行。比如初始化脚本如果在运行过程中被再次执行,可能导致系统状态异常。
-
竞争条件:当多个实例同时操作相同资源时,可能产生难以预料的竞争条件,这类问题往往难以复现和调试。
2. 基础检测方法:PID文件机制
2.1 PID文件原理与实现
PID(Process ID)文件机制是最基础也是最常用的单实例控制方法。其核心思想是:脚本启动时创建一个包含自身进程ID的文件,运行期间持续检查该文件对应的进程是否存在,脚本结束时删除该文件。
下面是一个典型的PID文件实现:
bash复制#!/bin/bash
LOCKFILE="/var/run/myscript.pid"
# 检查PID文件是否存在
if [ -f "$LOCKFILE" ]; then
# 获取文件中的PID
PID=$(cat "$LOCKFILE")
# 检查该PID是否仍在运行
if ps -p $PID > /dev/null 2>&1; then
echo "脚本已经在运行中(PID: $PID),本次执行终止。"
exit 1
else
# PID文件存在但进程已结束,可能是上次异常退出
echo "警告:发现陈旧的PID文件,正在清理..."
rm -f "$LOCKFILE"
fi
fi
# 创建PID文件
echo $$ > "$LOCKFILE"
# 设置退出时自动清理PID文件
trap 'rm -f "$LOCKFILE"' EXIT
# 以下是脚本的主要逻辑
echo "脚本开始执行..."
sleep 20 # 模拟长时间运行的任务
echo "脚本执行完成。"
2.2 PID文件机制的优缺点分析
优点:
- 实现简单,不需要额外依赖
- 对系统性能影响极小
- 适用于大多数单机场景
缺点:
- 在异常退出时可能遗留陈旧的PID文件(可以通过trap命令设置信号处理来缓解)
- 不适用于分布式环境
- 存在极小的竞争条件风险(在检查和创建PID文件的间隙)
提示:在实际应用中,PID文件通常存放在/var/run目录下,这个目录是Linux系统专门为守护进程存放运行时文件的目录。确保运行脚本的用户对该目录有写权限。
2.3 增强型PID文件实现
为了应对异常退出的情况,我们可以增强PID文件机制的健壮性:
bash复制#!/bin/bash
LOCKFILE="/var/run/myscript.pid"
MAX_AGE=3600 # PID文件最大年龄(秒)
# 检查并清理陈旧的PID文件
if [ -f "$LOCKFILE" ]; then
PID=$(cat "$LOCKFILE")
if ! ps -p $PID > /dev/null 2>&1; then
# 进程不存在,检查文件年龄
if [ $(($(date +%s) - $(date +%s -r "$LOCKFILE"))) -gt $MAX_AGE ]; then
echo "清理超过${MAX_AGE}秒的陈旧PID文件..."
rm -f "$LOCKFILE"
else
echo "脚本可能在运行中(PID: $PID),请稍后再试。"
exit 1
fi
else
echo "脚本已经在运行中(PID: $PID),本次执行终止。"
exit 1
fi
fi
# 创建PID文件
echo $$ > "$LOCKFILE"
trap 'rm -f "$LOCKFILE"' EXIT TERM INT HUP
# 脚本主体...
这个增强版本增加了对PID文件"年龄"的检查,可以自动清理超过一定时间的陈旧PID文件,避免了因脚本异常退出导致的长期锁死问题。
3. 高级锁机制:flock命令详解
3.1 flock命令原理与基本用法
flock是Linux系统提供的一个专门用于文件锁定的工具,它实现了更可靠的咨询锁机制。与PID文件相比,flock的最大优势是系统会在文件描述符关闭时自动释放锁,即使进程异常退出也不会留下死锁。
基本语法:
bash复制flock [options] <file|directory> <command>
或者用在脚本内部:
bash复制exec 200>"$LOCKFILE"
flock -n 200 || {
echo "无法获取锁,脚本可能正在运行。"
exit 1
}
3.2 flock的多种使用模式
非阻塞模式(-n参数):
bash复制flock -n /tmp/myscript.lock -c "your_command"
如果无法立即获取锁,立即失败返回。
超时模式(-w参数):
bash复制flock -w 30 /tmp/myscript.lock -c "your_command"
等待最多30秒获取锁,超时后失败。
共享锁模式(-s参数):
bash复制flock -s /tmp/myscript.lock -c "your_command"
获取共享锁(读锁),允许多个进程同时持有共享锁。
3.3 完整的flock实现示例
下面是一个在生产环境中经过验证的flock实现:
bash复制#!/bin/bash
LOCK_FILE="/tmp/myscript.lock"
TIMEOUT=60 # 等待锁的超时时间(秒)
# 使用文件描述符200
exec 200>"$LOCK_FILE"
# 尝试获取锁,带超时
if ! flock -w $TIMEOUT 200; then
echo "无法在${TIMEOUT}秒内获取锁,脚本可能正在运行。"
exit 1
fi
# 获取锁成功,记录PID
echo $$ > "$LOCK_FILE"
# 设置信号处理,确保退出时释放锁
trap 'flock -u 200; rm -f "$LOCK_FILE"' EXIT
# 脚本主体逻辑
echo "成功获取锁,开始执行..."
sleep 30 # 模拟长时间任务
echo "执行完成。"
3.4 flock与PID文件机制的对比
| 特性 | flock | PID文件机制 |
|---|---|---|
| 可靠性 | 高(系统自动管理锁) | 中(依赖正确清理) |
| 异常退出处理 | 自动释放锁 | 可能遗留陈旧PID文件 |
| 分布式支持 | 可通过NFS等共享文件系统实现 | 不支持 |
| 实现复杂度 | 中等 | 简单 |
| 性能影响 | 极小 | 极小 |
| 锁等待功能 | 支持超时等待 | 不支持 |
在实际选择时,对于关键任务脚本建议使用flock,而对于简单的、短时间运行的脚本可以使用PID文件机制。
4. 信号处理与资源清理
4.1 Linux信号基础
在Unix-like系统中,信号是进程间通信的一种基本方式。当脚本被中断时(如用户按下Ctrl+C),系统会向脚本发送信号。常见的信号包括:
- SIGINT (2):中断信号,通常由Ctrl+C触发
- SIGTERM (15):终止信号,kill命令默认发送的信号
- SIGHUP (1):挂起信号,终端断开时发送
- SIGKILL (9):强制终止信号,不可被捕获或忽略
4.2 在脚本中处理信号
合理的信号处理可以确保脚本在意外终止时也能正确清理资源。下面是一个信号处理的完整示例:
bash复制#!/bin/bash
LOCKFILE="/var/run/myscript.pid"
LOG_FILE="/var/log/myscript.log"
# 清理函数
cleanup() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - 收到信号,正在清理..." >> "$LOG_FILE"
[ -f "$LOCKFILE" ] && rm -f "$LOCKFILE"
# 其他清理操作...
exit 1
}
# 注册信号处理
trap cleanup INT TERM EXIT
# 创建PID文件
echo $$ > "$LOCKFILE"
# 脚本主体
echo "$(date '+%Y-%m-%d %H:%M:%S') - 脚本开始执行" >> "$LOG_FILE"
# 模拟长时间任务
for i in {1..10}; do
echo "正在处理第 $i 部分..." >> "$LOG_FILE"
sleep 5
done
echo "$(date '+%Y-%m-%d %H:%M:%S') - 脚本正常完成" >> "$LOG_FILE"
4.3 信号处理的最佳实践
- 总是处理常见信号:至少应该处理INT、TERM和EXIT信号
- 清理操作要幂等:确保清理函数可以安全地多次调用
- 避免在信号处理中执行耗时操作:信号处理函数应该尽快完成
- 记录信号事件:有助于后续的问题诊断
- 区分正常退出和信号退出:可以通过不同的退出码来区分
我曾经遇到过一个案例:一个数据库维护脚本没有正确处理SIGTERM信号,当系统管理员尝试正常终止它时,脚本直接退出而没有关闭数据库连接,导致数据库连接池耗尽。这个问题的排查花了我们整整一天时间。
5. 分布式环境下的锁机制
5.1 分布式锁的挑战
在分布式系统或多节点环境中,传统的单机锁机制不再适用。分布式锁需要解决以下问题:
- 互斥性:在任何时刻,只有一个客户端可以持有锁
- 死锁避免:即使持有锁的客户端崩溃,锁最终也能被释放
- 容错性:部分节点故障不影响整体可用性
- 性能:获取和释放锁的操作应该高效
5.2 基于Redis的分布式锁实现
Redis是实现分布式锁的常用工具,以下是使用Redis实现分布式锁的示例:
bash复制#!/bin/bash
REDIS_HOST="redis-server"
REDIS_PORT=6379
LOCK_KEY="myscript_lock"
LOCK_TIMEOUT=60 # 锁自动释放时间(秒)
# 尝试获取锁
acquire_lock() {
local result=$(redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" \
SET "$LOCK_KEY" "locked" NX EX "$LOCK_TIMEOUT")
[ "$result" = "OK" ] && return 0 || return 1
}
# 释放锁
release_lock() {
redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" \
DEL "$LOCK_KEY" >/dev/null
}
# 尝试获取锁
if ! acquire_lock; then
echo "无法获取分布式锁,脚本可能正在其他节点运行。"
exit 1
fi
# 确保退出时释放锁
trap 'release_lock' EXIT
# 脚本主体...
echo "成功获取分布式锁,开始执行..."
sleep 30
echo "执行完成。"
5.3 基于数据库的分布式锁
对于已经使用数据库的系统,可以利用数据库的唯一约束或事务特性实现分布式锁:
bash复制#!/bin/bash
DB_USER="user"
DB_PASS="password"
DB_NAME="app_db"
LOCK_NAME="maintenance_lock"
# 尝试获取锁
if ! mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" <<< \
"INSERT INTO distributed_locks (lock_name, owner, expires_at)
VALUES ('$LOCK_NAME', '$(hostname)', DATE_ADD(NOW(), INTERVAL 1 HOUR))
ON DUPLICATE KEY UPDATE
owner = IF(expires_at < NOW(), VALUES(owner), owner),
expires_at = IF(expires_at < NOW(), VALUES(expires_at), expires_at);" \
| grep -q "Query OK"; then
echo "无法获取数据库锁,脚本可能正在其他节点运行。"
exit 1
fi
# 确保退出时释放锁
trap 'mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" \
-e "DELETE FROM distributed_locks WHERE lock_name = '\'$LOCK_NAME\'' AND owner = '\'$(hostname)\''"' EXIT
# 脚本主体...
5.4 分布式锁方案比较
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Redis | 高性能,实现简单 | 需要额外维护Redis集群 | 高并发,短时间任务 |
| 数据库 | 无需额外组件 | 性能较低,增加数据库负担 | 已有数据库,低频长任务 |
| Zookeeper | 高可靠性,支持观察者模式 | 系统复杂,学习曲线陡峭 | 关键任务,需要高可靠性 |
| etcd | 强一致性,高可用 | 资源消耗较大 | Kubernetes环境,云原生应用 |
在实际项目中,我们曾经为金融系统实现过一个基于Redis和Zookeeper的双重锁机制:Redis锁用于高频的快速失败检查,Zookeeper锁用于关键操作的强一致性保证。这种组合方案既保证了性能,又确保了关键操作的安全性。
6. 实战案例:完整的脚本单例控制实现
6.1 生产级脚本模板
下面是一个集成了多种技术的生产环境可用脚本模板:
bash复制#!/bin/bash
# 配置部分
SCRIPT_NAME=$(basename "$0")
LOCK_DIR="/var/lock"
LOCK_FILE="$LOCK_DIR/$SCRIPT_NAME.lock"
LOG_DIR="/var/log/$SCRIPT_NAME"
LOG_FILE="$LOG_DIR/$(date +%Y%m%d).log"
MAX_RUNTIME=3600 # 最大允许运行时间(秒)
# 初始化目录
mkdir -p "$LOCK_DIR" "$LOG_DIR"
# 日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" >> "$LOG_FILE"
}
# 清理函数
cleanup() {
log "正在执行清理..."
# 释放文件锁
[ -n "$LOCK_FD" ] && flock -u "$LOCK_FD"
# 删除PID文件
[ -f "$LOCK_FILE.pid" ] && rm -f "$LOCK_FILE.pid"
log "清理完成,退出。"
exit ${1:-0}
}
# 信号处理
trap 'cleanup 1' INT TERM
# 获取文件锁
exec {LOCK_FD}>"$LOCK_FILE"
if ! flock -n "$LOCK_FD"; then
# 检查是否超过最大运行时间
if [ -f "$LOCK_FILE.pid" ]; then
PID=$(cat "$LOCK_FILE.pid")
if ps -p "$PID" >/dev/null 2>&1; then
START_TIME=$(stat -c %Y "$LOCK_FILE.pid")
CURRENT_TIME=$(date +%s)
if [ $((CURRENT_TIME - START_TIME)) -gt $MAX_RUNTIME ]; then
log "发现运行超过${MAX_RUNTIME}秒的陈旧进程(PID: $PID),强制终止..."
kill -9 "$PID"
sleep 1
# 再次尝试获取锁
if ! flock -n "$LOCK_FD"; then
log "仍然无法获取锁,退出。"
exit 1
fi
else
log "脚本已经在运行中(PID: $PID),退出。"
exit 1
fi
else
log "发现陈旧的PID文件,清理后继续..."
rm -f "$LOCK_FILE.pid"
# 再次尝试获取锁
if ! flock -n "$LOCK_FD"; then
log "无法获取锁,退出。"
exit 1
fi
fi
else
log "无法获取锁且没有PID文件,退出。"
exit 1
fi
fi
# 创建PID文件
echo $$ > "$LOCK_FILE.pid"
# 记录开始时间
START_TIME=$(date +%s)
log "=== 脚本开始执行 ==="
# 脚本主体
# 这里放置实际的业务逻辑
log "执行主要任务..."
sleep 30 # 模拟任务执行
log "任务完成。"
# 计算运行时间
END_TIME=$(date +%s)
RUNTIME=$((END_TIME - START_TIME))
log "脚本执行完成,总运行时间: ${RUNTIME}秒"
# 正常退出
cleanup 0
6.2 关键设计解析
- 多层级锁机制:同时使用flock文件锁和PID文件,提高可靠性
- 陈旧进程检测:自动检测并处理运行时间过长的实例
- 完善的日志记录:所有操作都有详细日志,便于问题排查
- 资源自动清理:通过trap确保各种情况下都能正确释放资源
- 运行时间监控:防止脚本无限期运行占用资源
这个模板在我负责的多个生产环境中运行良好,曾经帮助我们避免了多次潜在的重复执行事故。特别是在处理财务对账这种关键任务时,这种严谨的锁机制显得尤为重要。
7. 常见问题与解决方案
7.1 问题排查指南
问题1:脚本报告已经在运行,但实际上没有运行
- 检查:
ps aux | grep 脚本名确认确实没有运行 - 原因:可能是上次异常退出遗留了锁文件
- 解决:手动删除锁文件(
/var/run/script.pid或/tmp/script.lock)
问题2:无法创建PID文件
- 检查:运行脚本的用户对目标目录是否有写权限
- 解决:
sudo chmod a+w /var/run或改用/tmp目录
问题3:flock命令不可用
- 检查:
which flock - 解决:安装util-linux包(
yum install util-linux或apt-get install util-linux)
7.2 性能优化建议
- 锁文件位置:将锁文件放在内存文件系统(如/run或/tmp)中,减少磁盘IO
- 锁粒度控制:对于大型脚本,可以考虑对不同部分使用不同的锁,提高并发性
- 锁等待策略:根据业务需求设置合理的等待超时,避免无限等待
- 日志轮转:对于长期运行的脚本,实现日志轮转防止日志文件过大
7.3 跨平台兼容性处理
不同Unix-like系统在细节上可能有差异:
- flock可用性:BSD系统可能使用不同的锁机制
- 解决方案:检测系统类型,使用对应的锁命令
- /var/run目录:某些系统可能使用/var/run,而其他使用/run
- 解决方案:检测目录存在性,或使用平台无关的/tmp目录
- ps命令参数:不同系统的ps命令输出格式可能不同
- 解决方案:使用
ps -ef或ps aux等更通用的形式
- 解决方案:使用
下面是一个跨平台兼容的锁检查函数示例:
bash复制check_running() {
local pidfile="$1"
if [ -f "$pidfile" ]; then
local pid=$(cat "$pidfile")
if kill -0 "$pid" 2>/dev/null; then
echo "脚本已经在运行(PID: $pid)"
return 1
else
# 进程不存在,清理陈旧的PID文件
rm -f "$pidfile"
fi
fi
return 0
}
这个实现使用kill -0来检查进程是否存在,这种方法在大多数Unix系统上都可用,比解析ps输出更可靠。
8. 监控与告警集成
8.1 锁状态监控
对于关键业务脚本,应该实现锁状态的监控。可以通过以下方式实现:
- 锁文件时间检查:监控锁文件的修改时间,如果超过预期运行时间可能意味着僵死
- 进程活动检查:检查持有锁的进程是否还有实际活动(如日志输出)
- 心跳机制:长时间运行的脚本可以定期更新心跳文件
示例监控脚本:
bash复制#!/bin/bash
LOCK_FILE="/var/run/critical_script.lock"
MAX_AGE=7200 # 2小时
if [ -f "$LOCK_FILE" ]; then
FILE_AGE=$(($(date +%s) - $(stat -c %Y "$LOCK_FILE")))
if [ "$FILE_AGE" -gt "$MAX_AGE" ]; then
PID=$(cat "$LOCK_FILE")
# 发送告警
echo "警告:锁文件已存在${FILE_AGE}秒(PID: $PID)" | \
mail -s "脚本可能僵死" admin@example.com
# 可选:强制终止
kill -9 "$PID"
rm -f "$LOCK_FILE"
fi
fi
8.2 集成到现有监控系统
可以将锁检查集成到Nagios、Zabbix等监控系统中:
- Nagios插件示例:
bash复制#!/bin/bash
LOCK_FILE="$1"
WARNING_AGE=${2:-3600} # 默认1小时
CRITICAL_AGE=${3:-7200} # 默认2小时
if [ ! -f "$LOCK_FILE" ]; then
echo "OK: 无锁文件,脚本未运行"
exit 0
fi
AGE=$(($(date +%s) - $(stat -c %Y "$LOCK_FILE")))
PID=$(cat "$LOCK_FILE")
if ! kill -0 "$PID" 2>/dev/null; then
echo "CRITICAL: 发现陈旧的锁文件(PID: $PID)"
exit 2
fi
if [ "$AGE" -ge "$CRITICAL_AGE" ]; then
echo "CRITICAL: 脚本已运行${AGE}秒(PID: $PID)"
exit 2
elif [ "$AGE" -ge "$WARNING_AGE" ]; then
echo "WARNING: 脚本已运行${AGE}秒(PID: $PID)"
exit 1
else
echo "OK: 脚本正在运行中(PID: $PID, 运行${AGE}秒)"
exit 0
fi
- Zabbix监控项:
text复制UserParameter=script.lock.age[*], echo $(($(date +%s) - $(stat -c %Y "$1")))
8.3 可视化监控
对于复杂的脚本系统,可以建立可视化面板展示:
- 锁状态:正常/警告/危险
- 运行时长:当前实例运行了多长时间
- 历史记录:过去24小时的执行情况
- 资源使用:脚本占用的CPU/内存等资源
使用Grafana+Prometheus的组合可以很好地实现这种可视化。我曾经为一个ETL系统建立过这样的监控面板,当锁状态异常时,运维团队能立即收到告警并查看详细情况,大大减少了故障恢复时间。
9. 高级主题:锁的性能优化
9.1 锁争用问题分析
在高并发环境下,锁可能成为性能瓶颈。常见的锁争用问题包括:
- 热点锁:多个进程频繁竞争同一个锁
- 长持锁时间:持有锁的时间过长,导致其他进程长时间等待
- 锁粒度不当:锁的粒度过大或过小都会影响性能
我曾经优化过一个日志处理系统,原始版本使用全局锁,导致处理速度上不去。通过分析,我们将锁粒度细化到每个日志文件一个锁,性能提升了8倍。
9.2 锁优化策略
-
减小锁粒度:将一个大锁拆分为多个小锁
- 示例:处理多个文件时,为每个文件设置单独的锁
-
减少持锁时间:只在必要时持有锁
- 示例:读取配置时不加锁,只在写入时加锁
-
读写锁分离:区分读锁和写锁
- 读操作可以并发,写操作需要互斥
-
乐观锁机制:先操作,最后检查冲突
- 适用于冲突较少的情况
9.3 读写锁实现示例
使用flock实现简单的读写锁:
bash复制#!/bin/bash
LOCK_FILE="/tmp/data.lock"
READ_LOCK_FILE="/tmp/data.read.lock"
# 获取读锁
acquire_read_lock() {
exec {READ_FD}>"$READ_LOCK_FILE"
flock -s "$READ_FD" || return 1
# 检查是否有写锁
if [ -f "$LOCK_FILE" ]; then
flock -u "$READ_FD"
return 1
fi
return 0
}
# 获取写锁
acquire_write_lock() {
exec {WRITE_FD}>"$LOCK_FILE"
flock -x "$WRITE_FD" || return 1
# 检查是否有读锁
if [ -f "$READ_LOCK_FILE" ]; then
flock -u "$WRITE_FD"
return 1
fi
return 0
}
# 释放读锁
release_read_lock() {
[ -n "$READ_FD" ] && flock -u "$READ_FD"
}
# 释放写锁
release_write_lock() {
[ -n "$WRITE_FD" ] && flock -u "$WRITE_FD"
}
# 示例使用
if acquire_read_lock; then
echo "持有读锁,执行读取操作..."
sleep 2
release_read_lock
else
echo "无法获取读锁"
fi
if acquire_write_lock; then
echo "持有写锁,执行写入操作..."
sleep 5
release_write_lock
else
echo "无法获取写锁"
fi
9.4 锁性能测试方法
为了评估锁实现的性能,可以使用以下方法:
- 基准测试:测量不同并发度下的吞吐量
- 锁等待时间统计:记录获取锁的平均等待时间
- 竞争热点分析:识别最常发生竞争的锁
示例基准测试脚本:
bash复制#!/bin/bash
LOCK_FILE="/tmp/benchmark.lock"
THREADS=10
ITERATIONS=1000
# 测试flock性能
test_flock() {
for ((i=1; i<=$ITERATIONS; i++)); do
(
exec 200>"$LOCK_FILE"
if flock -n 200; then
echo "Thread $BASHPID acquired lock"
sleep 0.01
flock -u 200
fi
) &
done
wait
}
echo "开始测试flock性能..."
time test_flock
echo "测试完成"
在实际项目中,我们通常会使用更专业的工具如fio或自定义的微基准测试来评估锁性能。记住,任何优化都应该基于实际测量数据,而不是猜测。