← 中文资源

简体中文版本

锁公平性与饥饿

公平策略决定锁是授予等待最久的线程还是最新到达的线程,在吞吐与可预测延迟之间进行取舍。

阅读英文原文

概念概述

锁公平性与饥饿(Lock Fairness and Starvation)指的是:公平策略决定锁是授予等待最久的线程还是最新到达的线程,在吞吐与可预测延迟之间进行取舍。

从更严格的定义看,Lock fairness is the property that waiters acquire a lock in a bounded or first-come order, preventing a thread from being perpetually overtaken by newer arrivals. 该概念在工程实践中的价值,在于它能帮助团队用统一的语言描述一个具体的质量属性或设计约束,从而在评审与协作中减少歧义。

为什么需要它

It is a design choice for mutexes and queues where some callers must make progress under contention. 它与 Coroutines、Software Transactional Memory、Happens-Before Relationship 等概念经常一起出现,用于解决相近的工程问题。理解它的适用场景,有助于在多种备选方案之间做出有依据的取舍,而不是凭直觉选择技术方案。

常见误解

  • fair locks are always faster because they avoid barging
  • unfair locks guarantee progress for every thread

需要避免的反模式

  • defaulting to fair locks in a hot path without measuring throughput
  • allowing unbounded barging that starves a background task

实践要点

  1. An unfair lock lets an arriving thread grab the lock before a queued waiter, which improves throughput on multicore systems but can indefinitely delay a waiter.
  2. A fair lock transfers ownership in arrival order, reducing variance and latency spikes at the cost of additional handoff overhead.
  3. Choose per workload: interactive paths often benefit from fairness for predictable latency, while batch paths may prefer the higher throughput of barging.

参考来源

https://en.wikipedia.org/wiki/Starvation_(computer_science)

代码示例

#!/usr/bin/env bash
set -euo pipefail

lock_file=${1:-"${TMPDIR:-/tmp}/devexamples-backup.lock"}
max_wait=${LOCK_WAIT_SECONDS:-5}
work_file=""

cleanup() {
    local status=$?
    if [[ -n "$work_file" && -f "$work_file" ]]; then
        rm -f -- "$work_file"
    fi
    flock -u 9 2>/dev/null || true
    exit "$status"
}
trap cleanup EXIT

exec 9>"$lock_file"

if ! flock -w "$max_wait" 9; then
    printf 'could not acquire %s within %ss\n' "$lock_file" "$max_wait" >&2
    exit 75
fi

printf 'lock acquired by pid %d\n' "$"

work_file="$(mktemp "${TMPDIR:-/tmp}/backup.XXXXXX")"
printf 'job %d started at %s\n' "$" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$work_file"

sleep "${JOB_SECONDS:-1}"
cat -- "$work_file"
printf 'job %d finished; lock released on exit\n' "$"
-- PostgreSQL 会话级咨询锁:try 锁拿不到立即返回假
SELECT pg_try_advisory_lock(4271);

-- 若返回 true,此会话独占编号 4271 的锁
-- 其他实例的尝试会得到 false:
SELECT pg_try_advisory_lock(4271);  -- false:已被占用

-- 临界区:只有一个实例在执行对账
BEGIN;
SELECT pg_advisory_xact_lock(4271);  -- 事务级:提交或回滚自动释放

UPDATE reconciliation_runs
  SET started_at = now(), status = 'running'
  WHERE id = 'nightly-2026-10-21';

-- ... 对账主体 ...

UPDATE reconciliation_runs
  SET finished_at = now(), status = 'done'
  WHERE id = 'nightly-2026-10-21';
COMMIT;  -- 锁随事务结束自动归还

-- 会话级锁的显式释放路径
SELECT pg_advisory_unlock(4271);

-- 双键形式:类目 ID 加实体 ID 组合编号
SELECT pg_try_advisory_lock(4271, 42);

-- 锁状态观测
SELECT pg_locks.locktype, pg_locks.objid, pg_locks.granted
FROM pg_locks
WHERE locktype = 'advisory';