在 Bash 中使用 Flock,因此只发出一次请求
Using Flock in Bash so Request is Made Only Once
我正在尝试以
的方式配置我的脚本
- 如果某些数据不可用,请尝试获取它
- 如果另一个进程已经在获取它,请等待该进程完成
- 使用数据
我从 here 中找到了这个非常好的 flock 示例:
exec 200>$pidfile
flock -n 200 || exit 1
pid=$$
echo $pid 1>&200
如果无法获取锁(-n 标志),则失败。
我是否可以假设这意味着另一个文件已锁定 $pidfile
,我如何检测到锁定已在不同进程中释放?
我知道 wait $pid
会等到该进程完成,因此如果有某种方法可以记录当前持有锁的进程或仅检测解锁,以便其他进程知道数据可用,那么我认为这会起作用。
有什么想法吗?
if the lock cannot be immediately acquired, [in the absence of a -w
timeout], flock
waits until the lock is available
您可以使用 fuser
查看哪个进程持有文件句柄。
我的解决方案使用两个文件,pid.temp 和 data.temp:
backgroundGetData() {
local data=
# if global is null, check file.
if [ -z "$data" ]; then
data=$( cat $DATA_TEMP_FILE 2>/dev/null )
fi
# if file is empty, check process is making the request
if [ -z "$data" ]; then
for i in {1..5}; do
echo "INFO - Process: $BASHPID - Attempting to lock data temp file" >&2
local request_pid=$( cat $PID_FILE 2>/dev/null )
if [ -z "$request_pid" ]; then request_pid=0; fi
local exit_code=1
if [ "$request_pid" -eq 0 ]; then
( flock -n 200 || exit 1
echo "INFO - Process: $BASHPID - Fetching data." >&2
echo "$BASHPID">"$PID_FILE"
getData > $DATA_TEMP_FILE
) 200>$DATA_TEMP_FILE
exit_code=$?
fi
echo "INFO - Process: $BASHPID - returned $exit_code from lock attempt.">&2
[ $request_pid -ne 0 ] && echo "INFO - Process: $BASHPID - $request_pid is possibly locking">&2
if [ $exit_code -ne 0 ] && [ $request_pid -ne 0 ]; then
echo "INFO - Process: $BASHPID - waiting on $request_pid to complete">&2
tail --pid=${request_pid} -f /dev/null
echo "INFO - Process: $BASHPID - finished waiting.">&2
break
elif [ $exit_code -eq 0 ]; then break;
else
sleep 2
fi
done
data=$( cat $DATA_TEMP_FILE )
if [ -z "$data" ]; then
echo "WARN - Process: $BASHPID - Failed to retrieve data.">&2
fi
fi
echo "$least_loaded"
}
并且可以这样使用:
DATA=""
DATA_TEMP_FILE="data.temp"
PID_FILE="pid.temp"
$( backgroundGetData $DATA ) & ## Begin making request
doThing() {
if [ -z $DATA ]; then
# Redirect 3 to stdout, then use sterr in backgroundGetData to 3 so that
# logging messages can be shown and output can also be captued in variable.
exec 3>&1
DATA=$( backgroundGetData $DATA 2>&3)
fi
}
for job in "$jobs"; do
doThing &
done
它对我有用,但我不能 100% 确定它的安全性。
我正在尝试以
的方式配置我的脚本- 如果某些数据不可用,请尝试获取它
- 如果另一个进程已经在获取它,请等待该进程完成
- 使用数据
我从 here 中找到了这个非常好的 flock 示例:
exec 200>$pidfile
flock -n 200 || exit 1
pid=$$
echo $pid 1>&200
如果无法获取锁(-n 标志),则失败。
我是否可以假设这意味着另一个文件已锁定 $pidfile
,我如何检测到锁定已在不同进程中释放?
我知道 wait $pid
会等到该进程完成,因此如果有某种方法可以记录当前持有锁的进程或仅检测解锁,以便其他进程知道数据可用,那么我认为这会起作用。
有什么想法吗?
if the lock cannot be immediately acquired, [in the absence of a
-w
timeout],flock
waits until the lock is available
您可以使用 fuser
查看哪个进程持有文件句柄。
我的解决方案使用两个文件,pid.temp 和 data.temp:
backgroundGetData() {
local data=
# if global is null, check file.
if [ -z "$data" ]; then
data=$( cat $DATA_TEMP_FILE 2>/dev/null )
fi
# if file is empty, check process is making the request
if [ -z "$data" ]; then
for i in {1..5}; do
echo "INFO - Process: $BASHPID - Attempting to lock data temp file" >&2
local request_pid=$( cat $PID_FILE 2>/dev/null )
if [ -z "$request_pid" ]; then request_pid=0; fi
local exit_code=1
if [ "$request_pid" -eq 0 ]; then
( flock -n 200 || exit 1
echo "INFO - Process: $BASHPID - Fetching data." >&2
echo "$BASHPID">"$PID_FILE"
getData > $DATA_TEMP_FILE
) 200>$DATA_TEMP_FILE
exit_code=$?
fi
echo "INFO - Process: $BASHPID - returned $exit_code from lock attempt.">&2
[ $request_pid -ne 0 ] && echo "INFO - Process: $BASHPID - $request_pid is possibly locking">&2
if [ $exit_code -ne 0 ] && [ $request_pid -ne 0 ]; then
echo "INFO - Process: $BASHPID - waiting on $request_pid to complete">&2
tail --pid=${request_pid} -f /dev/null
echo "INFO - Process: $BASHPID - finished waiting.">&2
break
elif [ $exit_code -eq 0 ]; then break;
else
sleep 2
fi
done
data=$( cat $DATA_TEMP_FILE )
if [ -z "$data" ]; then
echo "WARN - Process: $BASHPID - Failed to retrieve data.">&2
fi
fi
echo "$least_loaded"
}
并且可以这样使用:
DATA=""
DATA_TEMP_FILE="data.temp"
PID_FILE="pid.temp"
$( backgroundGetData $DATA ) & ## Begin making request
doThing() {
if [ -z $DATA ]; then
# Redirect 3 to stdout, then use sterr in backgroundGetData to 3 so that
# logging messages can be shown and output can also be captued in variable.
exec 3>&1
DATA=$( backgroundGetData $DATA 2>&3)
fi
}
for job in "$jobs"; do
doThing &
done
它对我有用,但我不能 100% 确定它的安全性。