返回题库

一次性标志初始化

Once Flag Init

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

题目详情

在高频交易系统中,建立行情源连接涉及网络握手和认证,开销很大。为最小化启动延迟,系统常延迟此初始化到首次请求时。std::once_flag 与 call_once 提供线程安全的惰性初始化,确保无论多少线程同时触发,初始化仅执行一次。

任务:实现 LazyInit 类,使用 std::once_flag 和 std::call_once 实现行情源连接的惰性初始化。多线程可同时调用 connect(),仅首次调用执行实际连接。

英文原题

In high-frequency trading systems, establishing connections to market data feeds is an expensive operation involving network handshakes and authentication. To minimize startup latency, systems often defer this initialization until the first piece of data is needed using a thread-safe lazy initialization pattern. Utilizing synchronization primitives like std::call_once ensures that concurrent strategy threads do not trigger redundant connection attempts or race conditions.
Task
Implement a Marke

解析

问题分析

std::once_flag 配合 std::call_once 确保多线程环境中某个初始化逻辑恰好执行一次,常用于延迟初始化的单例或全局资源。与双重检查锁定(DCL)不同,call_once 免除了正确实现 memory fence 的负担。

实现

class MarketDataFeed {
    static std::once_flag init_flag_;
    static std::shared_ptr<MarketDataFeed> instance_;
public:
    static std::shared_ptr<MarketDataFeed> get() {
        std::call_once(init_flag_, [] {
            instance_.reset(new MarketDataFeed("tcp://md.example.com:9000"));
        });
        return instance_;
    }
};

复杂度与边界

  • 时间复杂度:首次调用 O(初始化开销),后续 O(1) 检查原子标志
  • 空间复杂度:O(1)(once_flag 为 1 字)
  • 边界条件:(1) 初始化函数抛异常时标志不设置,下次调用重试 (2) call_once 中不可递归调用同一 once_flag (3) 程序结束时 instance_ 自动析构

英文解析

Analysis

`std::once_flag` with `std::call_once` ensures initialization logic executes exactly once in multi-threaded environments, commonly used for lazy-initialized singletons or global resources. Unlike double-checked locking (DCL), `call_once` eliminates the burden of correctly implementing memory fences.

Solution

class MarketDataFeed {
    static std::once_flag init_flag_;
    static std::shared_ptr<MarketDataFeed> instance_;
public:
    static std::shared_ptr<MarketDataFeed> get() {
        std::call_once(init_flag_, [] {
            instance_.reset(new MarketDataFeed("tcp://md.example.com:9000"));
        });
        return instance_;
    }
};

Complexity & Edge Cases

  • Time complexity: First call O(initialization cost); subsequent calls O(1) atomic flag check
  • Space complexity: O(1) (once_flag is 1 word)
  • Edge cases: (1) If init function throws, flag is not set and next call retries (2) Cannot recursively call same once_flag within call_once (3) instance_ auto-destructs at program end

Key Considerations

  1. Exception safety: If initialization function throws, once_flag remains unset and next call re-attempts; this is correct behavior but must be documented for callers
  2. Concurrent call blocking: Threads calling get() during initialization block until init completes; initialization duration directly impacts startup latency for all waiting threads
  3. Static initialization order: once_flag itself must be initialized before first use; use constexpr constructor or function-local static to guarantee initialization order
  4. Destruction order: once-initialized objects must outlive all threads that access them; static destruction order issues arise if threads exit during shutdown