【问题标题】:Using Flock in Bash so Request is Made Only Once在 Bash 中使用 Flock,因此请求只发出一次
【发布时间】:2018-12-06 02:16:14
【问题描述】:

我正在尝试以这样的方式配置我的脚本

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

我从here 发现了这个非常好的羊群示例:

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

如果它不能获得锁(-n 标志),这将失败。

我可以假设这意味着另一个文件已经锁定了$pidfile,我如何才能检测到锁定已经在不同的进程中被释放?

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

有什么想法吗?

【问题讨论】:

    标签: bash shell concurrency parallel-processing flock


    【解决方案1】:

    根据flock (1) man page

    如果不能立即获得锁,[在没有-w 超时的情况下],flock 等待直到锁可用

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

    【讨论】:

    • 我已经添加了我目前的尝试,我认为可以实现我需要的功能,但是现在每个进程都无法锁定文件。
    • 我没有详细检查你的代码,但是拥有两个锁文件似乎是导致死锁的灾难性秘诀,特别是因为你只使用了 @987654326 中的 innerelse 分支中的@ 分支。只需锁定一件事,并确保在没有获得该锁定的情况下,您的任何代码都不会触及任何关键资源。
    • 意识到这一点,现在尝试只锁定一个资源。
    【解决方案2】:

    我的解决方案使用两个文件,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% 确定它的安全性。

    【讨论】:

      猜你喜欢
      • 2021-05-18
      • 2015-06-22
      • 2022-08-09
      • 2013-02-24
      • 2020-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多