确认公平读写者票锁的并发能力及设计逻辑合理性
公平共享票锁的设计验证与并发能力分析
问题描述
我正在实现一款简单的公平共享票锁,目标是让到达的读者和写者都能获得公平对待,核心规则如下:
- 所有请求者进入同一队列
- 队列中连续的读者组可并发访问
- 写者需等待其前方所有读者完成后再获取访问权
- 写者解锁后,下一组连续读者可获取访问权
代码实现
// SharedTicketLock.h #pragma once #include <atomic> #include <thread> class SharedTicketLock { std::atomic<uint16_t> next_ticket{0}; std::atomic<uint16_t> now_serving{0}; std::atomic<uint16_t> served{0}; public: void writer_lock(){ uint16_t my_ticket = next_ticket.fetch_add(2); while (now_serving.load() != my_ticket || now_serving.load() != served.load()){ std::this_thread::yield(); } } void reader_lock(){ uint16_t my_ticket = next_ticket.fetch_add(1); while (now_serving.load() != my_ticket){ std::this_thread::yield(); } now_serving.fetch_add(1); } void reader_unlock(){ served.fetch_add(1); } void writer_unlock(){ now_serving.fetch_add(2); served.fetch_add(2); } };
设计思路
- 在传统票锁基础上新增原子计数器
served,用于跟踪已离开临界区的请求者数量 - 写者获取2个票号(
next_ticket.fetch_add(2)),读者仅获取1个票号 - 读者在
now_serving等于自身票号时进入临界区,进入后立即递增now_serving,允许后续同组读者并发进入 - 写者需同时满足
now_serving等于自身票号且now_serving == served时才能进入,确保前方所有请求者已完全离开临界区 - 写者加锁时不递增
now_serving,通过2个票号的间隔阻止后续请求者提前进入 - 读者解锁时递增
served,让后续写者能判断前方读者是否全部完成 - 写者解锁时同时递增
now_serving和served各2,通知后续请求者写者已释放锁 - 依赖uint16_t的自然溢出,通过相等判断处理票号循环问题
疑问
- 该锁的并发能力如何?
- 上述设计推理是否合理?
设计合理性与并发能力分析
一、设计推理的合理性判断
整体设计符合公平性核心要求,通过票号间隔区分读者/写者权限,实现了队列化的公平调度:
- 公平性保障:所有请求者通过
next_ticket按到达顺序获取票号,严格遵循先到先服务,不会出现写者或读者饥饿的情况 - 读者并发逻辑:连续读者的票号连续递增,第一个读者进入后立即推进
now_serving,后续读者只要票号匹配就能直接进入,完美实现同组读者的并发访问,符合规则要求 - 写者等待逻辑:写者票号为偶数(初始从0开始),需等待
served == now_serving确保前方所有读者已解锁离开,解锁后同步推进now_serving和served,让后续请求者继续执行,完全匹配规则3、4 - 溢出处理:利用uint16_t无符号整数的模2^16溢出特性,通过相等判断处理票号循环,逻辑可行(无符号整数溢出在C++中是定义良好的行为)
但设计存在几个潜在细节问题:
- 内存序优化空间:代码中所有原子操作使用默认的
memory_order_seq_cst,虽保证顺序一致性,但性能冗余,可将load改为memory_order_acquire、fetch_add改为memory_order_release,不影响正确性但能降低同步开销 - 循环等待的效率:仅使用
yield()在高并发场景下可能导致频繁线程切换,可考虑加入短暂休眠(如std::this_thread::sleep_for(std::chrono::nanoseconds(10)))平衡CPU占用与响应速度
二、并发能力分析
- 读者并发效率:同一组读者可完全并发进入临界区,仅需等待票号匹配(而非前序读者解锁),并发能力接近无锁读者模式,仅存在原子操作的微小开销
- 写者性能特性:写者需等待前方所有读者完成,这是公平读写锁的固有特性,此处通过
served精准跟踪已完成请求,等待逻辑高效,无额外冗余判断 - 全局竞争点:
next_ticket的fetch_add是唯一全局竞争点,这是票锁类结构的固有问题,但相较于其他公平读写锁,该实现的竞争规模已处于较低水平 - 整体吞吐量:在读者占比高的场景下,吞吐量接近普通读写锁;写者占比高时,吞吐量与公平互斥锁相当,符合公平锁的性能预期
三、总结
该设计核心逻辑合理,完全满足设定的公平性与访问规则,并发能力在公平读写锁中处于较好水平,仅需针对内存序和等待逻辑做细节优化即可进一步提升性能。
内容的提问来源于stack exchange,提问作者Michael220
相关产品推荐
相关产品推荐

