返回题库

计数信号量限流

Counting Semaphore Throttle

专题
Systems & Architecture / 系统与架构
难度
L2
来源
MyntBit

题目详情

在高频交易中,到交易所的出站连接必须严格限制以避免处罚或断连。使用计数信号量管理连接槽位和排队等待是限制并发订单的常见方案。

任务:实现 SemaphoreThrottle 类,使用计数信号量限制并发连接数。acquire() 占用一个槽位(若满则阻塞等待),release() 释放槽位。支持配置最大连接数和等待超时。

英文原题

In high-frequency trading (HFT), outbound connections to an exchange must be strictly limited to avoid penalties or disconnections. A common approach to throttle concurrent orders is using a counting semaphore to manage connection slots and queue waiting orders. Simulating this behavior ensures deterministic testing of order flow management systems across multiple exchanges.
Task
Implement a ConnectionThrottle class to simulate a counting semaphore for multiple exchanges.

  • ConnectionThrottle(
解析

问题分析

C++20 std::counting_semaphore 是轻量级并发原语,比互斥锁+条件变量组合开销更低。在请求限流中,信号量初始化为最大并发数,acquire 消耗一个许可,release 归还许可,超过上限时阻塞。

实现

class SemaphoreThrottle {
    std::counting_semaphore<> sem_;
    const int max_concurrent_;
public:
    explicit SemaphoreThrottle(int max) : sem_(max), max_concurrent_(max) {}
    bool tryAcquire() { return sem_.try_acquire(); }
    void acquire() { sem_.acquire(); }  // 阻塞直到有许可
    void release() { sem_.release(); }
};

// 使用: 在 API 网关中限制同时处理的订单请求数
SemaphoreThrottle throttle(100);
if (throttle.tryAcquire()) { processOrder(); throttle.release(); }
else { return "rate limited"; }

复杂度与边界

  • 时间复杂度:acquire/release O(1),无系统调用(在无竞争时)
  • 空间复杂度:O(1)(counting_semaphore 仅一个原子计数器)
  • 边界条件:(1) 多余的 release 会使计数器超出 max (2) 析构时若有等待者行为未定义 (3) try_acquire 非阻塞立即返回

英文解析

Analysis

C++20 `std::counting_semaphore` is a lightweight concurrency primitive with lower overhead than mutex+condition_variable combinations. In request throttling, the semaphore is initialized to max concurrency; acquire consumes a permit, release returns one. Blocking occurs when permits exceed the limit.

Solution

class SemaphoreThrottle {
    std::counting_semaphore<> sem_;
    const int max_concurrent_;
public:
    explicit SemaphoreThrottle(int max) : sem_(max), max_concurrent_(max) {}
    bool tryAcquire() { return sem_.try_acquire(); }
    void acquire() { sem_.acquire(); }  // blocks until permit available
    void release() { sem_.release(); }
};

// Usage: limit concurrent order requests at API gateway
SemaphoreThrottle throttle(100);
if (throttle.tryAcquire()) { processOrder(); throttle.release(); }
else { return "rate limited"; }

Complexity & Edge Cases

  • Time complexity: acquire/release O(1), no system calls when uncontested
  • Space complexity: O(1) (counting_semaphore is a single atomic counter)
  • Edge cases: (1) Extra release calls increment counter beyond max (2) Undefined behavior if waiters exist during destruction (3) try_acquire may spuriously return false

Key Considerations

  1. Permits vs tokens: Semaphore permits represent concurrent execution slots; throttle rate = permits / time_unit — release timing determines effective rate
  2. Acquire blocking: When all permits are held, acquire() blocks the calling thread; blocking duration depends on release frequency — set permit count to match target throughput
  3. Fairness mode: Some semaphore implementations guarantee FIFO acquire ordering; unfair implementations may cause thread starvation under high contention
  4. Permit recovery: If a thread holding a permit crashes without release, semaphore permanently loses capacity; use try_acquire with timeout and guard-based release