在 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 会等到该进程完成,因此如果有某种方法可以记录当前持有锁的进程或仅检测解锁,以便其他进程知道数据可用,那么我认为这会起作用。

有什么想法吗?

根据 flock (1) man page

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% 确定它的安全性。