概念定义
读写锁区分两种访问模式:任意数量的读者可同时持有锁,而写者需要独占访问并排斥所有读者。它刻画了数据被读取远多于被修改的常见模式。相对普通互斥锁,收益是读者之间的并发;代价是额外的簿记开销、在持续读压下写者可能饿死的风险,以及需要策略决定谁先获得锁。
为什么重要
许多共享结构——路由表、特性开关、缓存——在每次请求时被读取,却偶尔才被写入。普通互斥锁把这些读取串行化,随核心数与流量增长成为瓶颈。读写锁恢复并行的读吞吐,同时让更新保持安全,这往往就是“可扩展的锁”与“不可扩展的锁”之间的差别。
工程实践
仅用于确实读占主导的状态;当写很频繁时,普通互斥锁或无锁方案更简单也更快。优先选用公平或偏向写者的实现,使突发的读者不会饿死更新。绝不要就地升级读锁——应以写模式释放并重新获取,同时重新校验数据。可考虑使用写时复制的不可变快照,让读者在完全不需要锁的情况下继续。
代码示例
export type Release = () => void;
export class ReadWriteLock {
private readers = 0;
private writer = false;
private waitingReaders: Array<() => void> = [];
private waitingWriters: Array<() => void> = [];
async acquireRead(): Promise<Release> {
if (!this.writer && this.waitingWriters.length === 0) {
this.readers += 1;
return this.createReadRelease();
}
await new Promise<void>((resolve) => this.waitingReaders.push(resolve));
return this.createReadRelease();
}
async acquireWrite(): Promise<Release> {
if (!this.writer && this.readers === 0 && this.waitingWriters.length === 0) {
this.writer = true;
return this.createWriteRelease();
}
await new Promise<void>((resolve) => this.waitingWriters.push(resolve));
return this.createWriteRelease();
}
private createReadRelease(): Release {
let released = false;
return () => {
if (released) return;
released = true;
this.readers -= 1;
if (this.readers === 0 && this.waitingWriters.length > 0) {
this.writer = true;
this.waitingWriters.shift()!();
}
};
}
private createWriteRelease(): Release {
let released = false;
return () => {
if (released) return;
released = true;
this.writer = false;
if (this.waitingWriters.length > 0) {
this.writer = true;
this.waitingWriters.shift()!();
return;
}
const readers = this.waitingReaders.splice(0);
this.readers += readers.length;
for (const resolve of readers) resolve();
};
}
get state(): {
readers: number;
writer: boolean;
waitingReaders: number;
waitingWriters: number;
} {
return {
readers: this.readers,
writer: this.writer,
waitingReaders: this.waitingReaders.length,
waitingWriters: this.waitingWriters.length,
};
}
}
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func newHardenedServer(addr string) *http.Server {
return &http.Server{
Addr: addr,
ReadHeaderTimeout: 5 * time.Second, // 头部必须快速到达
ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 << 20, // 头部上限 1MiB
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.Header().Set("Allow", http.MethodPost)
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
fmt.Fprintln(w, "accepted")
}),
}
}
func main() {
srv := newHardenedServer(":8080")
log.Fatal(srv.ListenAndServe())
}