在Bash中使用Flock,因此仅发出一次请求

时间:2018-06-27 08:52:10

标签: bash shell concurrency parallel-processing flock

我正在尝试以以下方式配置脚本

  • 如果某些数据不可用,请尝试获取它们
  • 如果另一个进程已经在获取它,请等待该进程完成
  • 使用数据

here那里我发现了一个非常不错的羊群例子:

exec 200>$pidfile
flock -n 200 || exit 1
pid=$$
echo $pid 1>&200

如果无法获取锁(-n标志),则失败。

我是否可以假定这意味着另一个文件已锁定$pidfile,并且如何检测到该锁定已在其他过程中释放?

我知道wait $pid会等到该过程完成为止,因此,如果有某种方法可以记录当前哪个进程持有锁,或者只是检测到解锁,以便其他进程知道数据可用,那么我认为这会起作用。

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

根据flock (1) man page

  

如果无法立即获取锁,[在没有-w超时的情况下,flock将等待直到锁可用为止

您可以使用fuser查看哪个进程持有文件句柄。

答案 1 :(得分:0)

我的解决方案使用两个文件pid.temp和data.temp:

backgroundGetData() {
    local data=$1

    # 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%的安全性。