19_apollo_cyber_base子模块软件架构分析
1. 概述
apollo_cyber_base子模块是Apollo Cyber RT中间件的核心基础组件库,提供了高性能并发编程所需的基础数据结构和同步原语。该模块实现了无锁哈希表、原子读写锁、有界队列、对象池、线程池等关键组件,采用现代C++11/14标准和原子操作实现,确保了在高并发场景下的性能和正确性。模块通过精心设计的内存对齐和缓存行优化,减少了伪共享问题,提升了多线程环境下的执行效率,为Cyber RT的通信、调度和任务管理提供了坚实的基础设施支持。
2. 软件架构图
graph TB
subgraph "并发原语层"
C1[AtomicRWLock]
C2[ReentrantRWLock]
C3[PthreadRWLock]
C4[RWLockGuard]
end
subgraph "数据结构层"
D1[AtomicHashMap]
D2[BoundedQueue]
D3[UnboundedQueue]
D4[ArenaQueue]
D5[ThreadSafeQueue]
end
subgraph "内存管理层"
M1[ObjectPool]
M2[ConcurrentObjectPool]
end
subgraph "线程管理层"
T1[ThreadPool]
T2[WaitStrategy]
T3[Signal]
end
subgraph "工具层"
U1[Macros]
U2[ForEach]
U3[WaitStrategy]
end
subgraph "应用层"
A1[通信模块]
A2[调度模块]
A3[任务管理]
A4[消息队列]
end
C1 --> A1
C2 --> A2
C3 --> A3
D1 --> A1
D2 --> A4
D3 --> A4
D4 --> A4
D5 --> A4
M1 --> A2
M2 --> A3
T1 --> A2
T2 --> D2
T3 --> T1
U1 --> D2
U2 --> M1
U3 --> D2
style C1 fill:#e1f5fe
style D1 fill:#f3e5f5
style M1 fill:#fff3e0
style T1 fill:#e8f5e8
3. 调用流程图
sequenceDiagram
participant App as 应用程序
participant TP as ThreadPool
participant BQ as BoundedQueue
participant WS as WaitStrategy
participant OP as ObjectPool
participant AHW as AtomicHashMap
participant ARW as AtomicRWLock
App->>TP: 创建线程池
TP->>BQ: 初始化任务队列
BQ->>WS: 设置等待策略
WS-->>BQ: 策略设置完成
BQ-->>TP: 队列初始化完成
TP->>TP: 创建工作线程
TP-->>App: 线程池创建完成
App->>TP: 提交任务
TP->>OP: 获取任务对象
OP->>OP: 从对象池分配
OP-->>TP: 返回任务对象
TP->>BQ: 入队任务
BQ->>WS: 检查队列状态
WS->>WS: 等待或通知
WS-->>BQ: 状态检查完成
BQ-->>TP: 入队完成
TP-->>App: 返回Future
TP->>TP: 工作线程执行
TP->>BQ: 出队任务
BQ->>WS: 检查队列状态
WS->>WS: 等待或通知
WS-->>BQ: 状态检查完成
BQ-->>TP: 返回任务
TP->>TP: 执行任务
TP->>OP: 释放任务对象
OP->>OP: 归还到对象池
OP-->>TP: 释放完成
App->>AHW: 存储键值对
AHW->>AHW: 计算哈希索引
AHW->>AHW: 查找或插入Entry
AHW->>AHW: 原子操作更新
AHW-->>App: 存储完成
App->>ARW: 获取读锁
ARW->>ARW: 检查写锁等待
ARW->>ARW: 原子增加读锁计数
ARW-->>App: 读锁获取成功
App->>ARW: 释放读锁
ARW->>ARW: 原子减少读锁计数
ARW-->>App: 读锁释放完成
App->>ARW: 获取写锁
ARW->>ARW: 增加写锁等待计数
ARW->>ARW: 等待读锁释放
ARW->>ARW: 原子设置写锁状态
ARW->>ARW: 减少写锁等待计数
ARW-->>App: 写锁获取成功
App->>ARW: 释放写锁
ARW->>ARW: 原子释放写锁
ARW-->>App: 写锁释放完成
4. UML类图
4.1 核心数据结构类图
classDiagram
class AtomicHashMap {
-K key
-V value
-Bucket table_[TableSize]
-uint64_t capacity_
-uint64_t mode_num_
+Has(K key) bool
+Get(K key, V** value) bool
+Get(K key, V* value) bool
+Set(K key) void
+Set(K key, const V& value) void
+Set(K key, V&& value) void
}
class Entry {
-K key
-atomic~V*~ value_ptr
-atomic~Entry*~ next
+Entry(K key)
+Entry(K key, const V& value)
+Entry(K key, V&& value)
+~Entry()
}
class Bucket {
-Entry* head_
+Bucket()
+~Bucket()
+Has(K key) bool
+Find(K key, Entry** prev_ptr, Entry** target_ptr) bool
+Insert(K key, const V& value) void
+Insert(K key, V&& value) void
+Insert(K key) void
+Get(K key, V** value) bool
}
class BoundedQueue {
-atomic~uint64_t~ head_
-atomic~uint64_t~ tail_
-atomic~uint64_t~ commit_
-uint64_t pool_size_
-T* pool_
-unique_ptr~WaitStrategy~ wait_strategy_
-volatile bool break_all_wait_
+Init(uint64_t size) bool
+Init(uint64_t size, WaitStrategy* strategy) bool
+Enqueue(const T& element) bool
+Enqueue(T&& element) bool
+WaitEnqueue(const T& element) bool
+WaitEnqueue(T&& element) bool
+Dequeue(T* element) bool
+WaitDequeue(T* element) bool
+Size() uint64_t
+Empty() bool
+SetWaitStrategy(WaitStrategy* strategy) void
+BreakAllWait() void
-GetIndex(uint64_t num) uint64_t
}
class ObjectPool {
-uint32_t num_objects_
-char* object_arena_
-Node* free_head_
+ObjectPool(uint32_t num_objects, Args... args)
+ObjectPool(uint32_t num_objects, InitFunc f, Args... args)
+~ObjectPool()
+GetObject() shared_ptr~T~
-ReleaseObject(T* object) void
}
class Node {
+T object
+Node* next
}
class ThreadPool {
-vector~thread~ workers_
-BoundedQueue~function~void()~~ task_queue_
-atomic_bool stop_
+ThreadPool(size_t thread_num, size_t max_task_num)
+Enqueue(F&& f, Args&&... args) future
+~ThreadPool()
}
class AtomicRWLock {
-atomic~uint32_t~ write_lock_wait_num_
-atomic~int32_t~ lock_num_
-bool write_first_
+ReadLock() void
+WriteLock() void
+ReadUnlock() void
+WriteUnlock() void
}
AtomicHashMap --> Entry
AtomicHashMap --> Bucket
Bucket --> Entry
BoundedQueue --> WaitStrategy
ObjectPool --> Node
ThreadPool --> BoundedQueue
4.2 等待策略类图
classDiagram
class WaitStrategy {
<<abstract>>
+NotifyOne() void
+BreakAllWait() void
+EmptyWait() bool
}
class BlockWaitStrategy {
-mutex mutex_
-condition_variable cond_
-bool break_all_wait_
+NotifyOne() void
+BreakAllWait() void
+EmptyWait() bool
}
class SleepWaitStrategy {
+NotifyOne() void
+BreakAllWait() void
+EmptyWait() bool
}
class YieldWaitStrategy {
+NotifyOne() void
+BreakAllWait() void
+EmptyWait() bool
}
class SpinWaitStrategy {
+NotifyOne() void
+BreakAllWait() void
+EmptyWait() bool
}
WaitStrategy <|-- BlockWaitStrategy
WaitStrategy <|-- SleepWaitStrategy
WaitStrategy <|-- YieldWaitStrategy
WaitStrategy <|-- SpinWaitStrategy
4.3 读写锁类图
classDiagram
class AtomicRWLock {
-atomic~uint32_t~ write_lock_wait_num_
-atomic~int32_t~ lock_num_
-bool write_first_
+ReadLock() void
+WriteLock() void
+ReadUnlock() void
+WriteUnlock() void
}
class ReentrantRWLock {
-AtomicRWLock lock_
-thread_local~int~ read_count_
-thread_local~int~ write_count_
+ReadLock() void
+WriteLock() void
+ReadUnlock() void
+WriteUnlock() void
}
class PthreadRWLock {
-pthread_rwlock_t lock_
+PthreadRWLock()
+~PthreadRWLock()
+ReadLock() void
+WriteLock() void
+ReadUnlock() void
+WriteUnlock() void
}
class ReadLockGuard {
-Lock& lock_
+ReadLockGuard(Lock& lock)
+~ReadLockGuard()
}
class WriteLockGuard {
-Lock& lock_
+WriteLockGuard(Lock& lock)
+~WriteLockGuard()
}
ReentrantRWLock --> AtomicRWLock
ReadLockGuard --> AtomicRWLock
ReadLockGuard --> ReentrantRWLock
ReadLockGuard --> PthreadRWLock
WriteLockGuard --> AtomicRWLock
WriteLockGuard --> ReentrantRWLock
WriteLockGuard --> PthreadRWLock
5. 状态机
5.1 BoundedQueue状态机
stateDiagram-v2
[*] --> UNINITIALIZED: 创建队列
UNINITIALIZED --> INITIALIZED: Init成功
UNINITIALIZED --> INIT_FAILED: Init失败
INIT_FAILED --> [*]: 销毁队列
INITIALIZED --> ENQUEUING: Enqueue调用
ENQUEUING --> ENQUEUED: 入队成功
ENQUEUING --> QUEUE_FULL: 队列已满
QUEUE_FULL --> INITIALIZED: 返回false
ENQUEUED --> INITIALIZED: 返回true
INITIALIZED --> WAIT_ENQUEUING: WaitEnqueue调用
WAIT_ENQUEUING --> WAITING: 等待队列空间
WAITING --> ENQUEUING: 队列有空间
WAITING --> WAIT_TIMEOUT: 等待超时
WAIT_TIMEOUT --> INITIALIZED: 返回false
WAITING --> BREAK_ALL: BreakAllWait调用
BREAK_ALL --> INITIALIZED: 返回false
INITIALIZED --> DEQUEUING: Dequeue调用
DEQUEUING --> DEQUEUED: 出队成功
DEQUEUING --> QUEUE_EMPTY: 队列为空
QUEUE_EMPTY --> INITIALIZED: 返回false
DEQUEUED --> INITIALIZED: 返回true
INITIALIZED --> WAIT_DEQUEUING: WaitDequeue调用
WAIT_DEQUEUING --> WAITING_DATA: 等待队列数据
WAITING_DATA --> DEQUEUING: 队列有数据
WAITING_DATA --> WAIT_TIMEOUT: 等待超时
WAIT_TIMEOUT --> INITIALIZED: 返回false
WAITING_DATA --> BREAK_ALL: BreakAllWait调用
BREAK_ALL --> INITIALIZED: 返回false
INITIALIZED --> DESTROYING: 析构调用
DESTROYING --> [*]: 销毁完成
note right of ENQUEUING: 原子操作更新tail
note right of DEQUEUING: 原子操作更新head
note right of WAITING: 使用WaitStrategy等待
note right of BREAK_ALL: 中断所有等待操作
5.2 AtomicRWLock状态机
stateDiagram-v2
[*] --> FREE: 创建锁
FREE --> READING: ReadLock成功
FREE --> WRITING: WriteLock成功
FREE --> WRITE_WAITING: WriteLock等待
READING --> READING: ReadLock成功
READING --> FREE: ReadUnlock
READING --> WRITE_WAITING: WriteLock等待
WRITE_WAITING --> WRITE_WAITING: WriteLock等待
WRITE_WAITING --> WRITING: ReadLock全部释放
WRITE_WAITING --> FREE: WriteLock取消
WRITING --> FREE: WriteUnlock
note right of READING: lock_num_ > 0
note right of WRITING: lock_num_ == WRITE_EXCLUSIVE
note right of WRITE_WAITING: write_lock_wait_num_ > 0
note right of FREE: lock_num_ == RW_LOCK_FREE
5.3 ObjectPool状态机
stateDiagram-v2
[*] --> INITIALIZING: 创建对象池
INITIALIZING --> INITIALIZED: 内存分配成功
INITIALIZING --> ALLOC_FAILED: 内存分配失败
ALLOC_FAILED --> [*]: 抛出异常
INITIALIZED --> ALLOCATING: GetObject调用
ALLOCATING --> ALLOCATED: 对象分配成功
ALLOCATING --> POOL_EMPTY: 对象池为空
POOL_EMPTY --> INITIALIZED: 返回nullptr
ALLOCATED --> INITIALIZED: 返回shared_ptr
INITIALIZED --> RELEASING: 对象析构
RELEASING --> RELEASING: ReleaseObject调用
RELEASING --> INITIALIZED: 对象归还池中
INITIALIZED --> DESTROYING: 析构调用
DESTROYING --> [*]: 销毁完成
note right of ALLOCATING: 从free_head_获取对象
note right of RELEASING: 对象归还到free_head_
note right of DESTROYING: 调用所有对象的析构函数
6. 源码分析
6.1 AtomicHashMap分析
6.1.1 类模板定义
AtomicHashMap是一个无锁固定大小的哈希表实现,使用原子操作确保线程安全。
template <typename K, typename V, std::size_t TableSize = 128,
typename std::enable_if<std::is_integral<K>::value &&
(TableSize & (TableSize - 1)) == 0,
int>::type = 0>
class AtomicHashMap {
public:
AtomicHashMap() : capacity_(TableSize), mode_num_(capacity_ - 1) {}
AtomicHashMap(const AtomicHashMap &other) = delete;
AtomicHashMap &operator=(const AtomicHashMap &other) = delete;
该类模板要求键类型K必须是整数类型,表大小TableSize必须是2的幂次方。构造函数初始化容量和掩码,掩码用于快速计算哈希索引。删除了拷贝构造和赋值操作符,防止意外拷贝。
6.1.2 Entry结构体
Entry结构体表示哈希表中的一个条目,包含键、值指针和下一个条目的指针。
private:
struct Entry {
Entry() {}
explicit Entry(K key) : key(key) {
value_ptr.store(new V(), std::memory_order_release);
}
Entry(K key, const V &value) : key(key) {
value_ptr.store(new V(value), std::memory_order_release);
}
Entry(K key, V &&value) : key(key) {
value_ptr.store(new V(std::forward<V>(value)), std::memory_order_release);
}
~Entry() { delete value_ptr.load(std::memory_order_acquire); }
K key = 0;
std::atomic<V *> value_ptr = {nullptr};
std::atomic<Entry *> next = {nullptr};
};
Entry结构体使用原子指针存储值和下一个条目,确保多线程环境下的安全访问。构造函数使用memory_order_release内存序,析构函数使用memory_order_acquire内存序,确保正确的内存可见性。
6.1.3 Bucket类
Bucket类表示哈希表中的一个桶,使用链表解决哈希冲突。
class Bucket {
public:
Bucket() : head_(new Entry()) {}
~Bucket() {
Entry *ite = head_;
while (ite) {
auto tmp = ite->next.load(std::memory_order_acquire);
delete ite;
ite = tmp;
}
}
bool Has(K key) {
Entry *m_target = head_->next.load(std::memory_order_acquire);
while (Entry *target = m_target) {
if (target->key < key) {
m_target = target->next.load(std::memory_order_acquire);
continue;
} else {
return target->key == key;
}
}
return false;
}
Bucket类维护一个带头节点的有序链表,Has方法遍历链表查找指定键。由于链表是有序的,查找时可以利用键的大小关系提前终止搜索。
6.1.4 Find方法
Find方法在链表中查找指定键,返回前驱节点和目标节点。
bool Find(K key, Entry **prev_ptr, Entry **target_ptr) {
Entry *prev = head_;
Entry *m_target = head_->next.load(std::memory_order_acquire);
while (Entry *target = m_target) {
if (target->key == key) {
*prev_ptr = prev;
*target_ptr = target;
return true;
} else if (target->key > key) {
*prev_ptr = prev;
*target_ptr = target;
return false;
} else {
prev = target;
m_target = target->next.load(std::memory_order_acquire);
}
}
*prev_ptr = prev;
*target_ptr = nullptr;
return false;
}
Find方法遍历有序链表,查找指定键。如果找到,返回true并设置前驱节点和目标节点;如果未找到,返回false并设置插入位置的前驱节点和目标节点(nullptr表示插入到链表末尾)。
6.1.5 Insert方法
Insert方法插入或更新键值对,使用CAS操作确保线程安全。
void Insert(K key, const V &value) {
Entry *prev = nullptr;
Entry *target = nullptr;
Entry *new_entry = nullptr;
V *new_value = nullptr;
while (true) {
if (Find(key, &prev, &target)) {
// key exists, update value
if (!new_value) {
new_value = new V(value);
}
auto old_val_ptr = target->value_ptr.load(std::memory_order_acquire);
if (target->value_ptr.compare_exchange_strong(
old_val_ptr, new_value, std::memory_order_acq_rel,
std::memory_order_relaxed)) {
delete old_val_ptr;
if (new_entry) {
delete new_entry;
new_entry = nullptr;
}
return;
}
continue;
} else {
if (!new_entry) {
new_entry = new Entry(key, value);
}
new_entry->next.store(target, std::memory_order_release);
if (prev->next.compare_exchange_strong(target, new_entry,
std::memory_order_acq_rel,
std::memory_order_relaxed)) {
// Insert success
if (new_value) {
delete new_value;
new_value = nullptr;
}
return;
}
// another entry has been inserted, retry
}
}
}
Insert方法使用CAS循环实现无锁插入。如果键已存在,更新值;否则插入新条目。CAS操作确保了并发插入的正确性,失败时重试。该方法避免了锁的使用,提高了并发性能。
6.2 AtomicRWLock分析
6.2.1 类定义
AtomicRWLock是一个基于原子操作的读写锁实现。
class AtomicRWLock {
friend class ReadLockGuard<AtomicRWLock>;
friend class WriteLockGuard<AtomicRWLock>;
public:
static const int32_t RW_LOCK_FREE = 0;
static const int32_t WRITE_EXCLUSIVE = -1;
static const uint32_t MAX_RETRY_TIMES = 5;
AtomicRWLock() {}
explicit AtomicRWLock(bool write_first) : write_first_(write_first) {}
private:
void ReadLock();
void WriteLock();
void ReadUnlock();
void WriteUnlock();
AtomicRWLock(const AtomicRWLock&) = delete;
AtomicRWLock& operator=(const AtomicRWLock&) = delete;
std::atomic<uint32_t> write_lock_wait_num_ = {0};
std::atomic<int32_t> lock_num_ = {0};
bool write_first_ = true;
};
该类使用两个原子变量:write_lock_wait_num_记录等待写锁的线程数,lock_num_记录锁状态。RW_LOCK_FREE表示锁空闲,WRITE_EXCLUSIVE表示写锁独占。write_first_标志控制写锁优先策略。
6.2.2 ReadLock实现
ReadLock方法获取读锁,支持写优先策略。
inline void AtomicRWLock::ReadLock() {
uint32_t retry_times = 0;
int32_t lock_num = lock_num_.load();
if (write_first_) {
do {
while (lock_num < RW_LOCK_FREE || write_lock_wait_num_.load() > 0) {
if (++retry_times == MAX_RETRY_TIMES) {
// saving cpu
std::this_thread::yield();
retry_times = 0;
}
lock_num = lock_num_.load();
}
} while (!lock_num_.compare_exchange_weak(lock_num, lock_num + 1,
std::memory_order_acq_rel,
std::memory_order_relaxed));
} else {
do {
while (lock_num < RW_LOCK_FREE) {
if (++retry_times == MAX_RETRY_TIMES) {
// saving cpu
std::this_thread::yield();
retry_times = 0;
}
lock_num = lock_num_.load();
}
} while (!lock_num_.compare_exchange_weak(lock_num, lock_num + 1,
std::memory_order_acq_rel,
std::memory_order_relaxed));
}
}
ReadLock方法使用CAS循环获取读锁。如果write_first_为true,读锁需要等待所有写锁等待者完成;否则只等待锁空闲。MAX_RETRY_TIMES控制自旋次数,超过后使用yield让出CPU,避免忙等待浪费CPU资源。
6.2.3 WriteLock实现
WriteLock方法获取写锁,需要等待所有读锁释放。
inline void AtomicRWLock::WriteLock() {
int32_t rw_lock_free = RW_LOCK_FREE;
uint32_t retry_times = 0;
write_lock_wait_num_.fetch_add(1);
while (!lock_num_.compare_exchange_weak(rw_lock_free, WRITE_EXCLUSIVE,
std::memory_order_acq_rel,
std::memory_order_relaxed)) {
// rw_lock_free will change after CAS fail, so init agin
rw_lock_free = RW_LOCK_FREE;
if (++retry_times == MAX_RETRY_TIMES) {
// saving cpu
std::this_thread::yield();
retry_times = 0;
}
}
write_lock_wait_num_.fetch_sub(1);
}
WriteLock方法首先增加写锁等待计数,然后使用CAS循环等待锁空闲并设置为写锁独占状态。获取成功后减少写锁等待计数。该方法确保了写锁的独占性,防止读写并发。
6.3 BoundedQueue分析
6.3.1 类定义
BoundedQueue是一个有界队列实现,使用环形缓冲区和原子操作实现无锁并发。
template <typename T>
class BoundedQueue {
public:
using value_type = T;
using size_type = uint64_t;
public:
BoundedQueue() {}
BoundedQueue& operator=(const BoundedQueue& other) = delete;
BoundedQueue(const BoundedQueue& other) = delete;
~BoundedQueue();
bool Init(uint64_t size);
bool Init(uint64_t size, WaitStrategy* strategy);
bool Enqueue(const T& element);
bool Enqueue(T&& element);
bool WaitEnqueue(const T& element);
bool WaitEnqueue(T&& element);
bool Dequeue(T* element);
bool WaitDequeue(T* element);
uint64_t Size();
bool Empty();
void SetWaitStrategy(WaitStrategy* WaitStrategy);
void BreakAllWait();
uint64_t Head() { return head_.load(); }
uint64_t Tail() { return tail_.load(); }
uint64_t Commit() { return commit_.load(); }
private:
uint64_t GetIndex(uint64_t num);
alignas(CACHELINE_SIZE) std::atomic<uint64_t> head_ = {0};
alignas(CACHELINE_SIZE) std::atomic<uint64_t> tail_ = {1};
alignas(CACHELINE_SIZE) std::atomic<uint64_t> commit_ = {1};
uint64_t pool_size_ = 0;
T* pool_ = nullptr;
std::unique_ptr<WaitStrategy> wait_strategy_ = nullptr;
volatile bool break_all_wait_ = false;
};
该类使用三个原子变量:head_表示出队位置,tail_表示入队位置,commit_表示已提交位置。使用alignas(CACHELINE_SIZE)确保每个变量位于不同的缓存行,避免伪共享。WaitStrategy提供了不同的等待策略。
6.3.2 Init方法
Init方法初始化队列,分配环形缓冲区。
template <typename T>
bool BoundedQueue<T>::Init(uint64_t size, WaitStrategy* strategy) {
// Head and tail each occupy a space
pool_size_ = size + 2;
pool_ = reinterpret_cast<T*>(std::calloc(pool_size_, sizeof(T)));
if (pool_ == nullptr) {
return false;
}
for (uint64_t i = 0; i < pool_size_; ++i) {
new (&(pool_[i])) T();
}
wait_strategy_.reset(strategy);
return true;
}
Init方法分配pool_size_个T对象的内存,使用placement new构造每个对象。pool_size_比请求大小大2,因为head和tail各占用一个空间。WaitStrategy被设置为提供的策略对象。
6.3.3 Enqueue方法
Enqueue方法入队元素,使用CAS操作确保线程安全。
template <typename T>
bool BoundedQueue<T>::Enqueue(const T& element) {
uint64_t new_tail = 0;
uint64_t old_commit = 0;
uint64_t old_tail = tail_.load(std::memory_order_acquire);
do {
new_tail = old_tail + 1;
if (GetIndex(new_tail) == GetIndex(head_.load(std::memory_order_acquire))) {
return false;
}
} while (!tail_.compare_exchange_weak(old_tail, new_tail,
std::memory_order_acq_rel,
std::memory_order_relaxed));
pool_[GetIndex(old_tail)] = element;
do {
old_commit = old_tail;
} while (cyber_unlikely(!commit_.compare_exchange_weak(
old_commit, new_tail, std::memory_order_acq_rel,
std::memory_order_relaxed)));
wait_strategy_->NotifyOne();
return true;
}
Enqueue方法使用两阶段CAS操作:第一阶段CAS更新tail_,第二阶段CAS更新commit_。如果队列已满(new_tail的索引等于head_的索引),返回false。成功入队后通知等待的消费者。
6.3.4 Dequeue方法
Dequeue方法出队元素,使用CAS操作确保线程安全。
template <typename T>
bool BoundedQueue<T>::Dequeue(T* element) {
uint64_t new_head = 0;
uint64_t old_head = head_.load(std::memory_order_acquire);
do {
new_head = old_head + 1;
if (new_head == commit_.load(std::memory_order_acquire)) {
return false;
}
*element = pool_[GetIndex(new_head)];
} while (!head_.compare_exchange_weak(old_head, new_head,
std::memory_order_acq_rel,
std::memory_order_relaxed));
return true;
}
Dequeue方法使用CAS操作更新head_,如果队列为空(new_head等于commit_),返回false。成功出队后返回true。该方法不需要通知生产者,因为生产者通过检查tail_和head_的关系判断队列是否已满。
6.3.5 GetIndex方法
GetIndex方法计算环形缓冲区的索引。
template <typename T>
inline uint64_t BoundedQueue<T>::GetIndex(uint64_t num) {
return num - (num / pool_size_) * pool_size_; // faster than %
}
该方法使用减法和乘法代替取模运算,提高了性能。由于pool_size_是常数,编译器可以优化该计算。
6.4 ObjectPool分析
6.4.1 类定义
ObjectPool是一个对象池实现,用于重用对象减少内存分配开销。
template <typename T>
class ObjectPool : public std::enable_shared_from_this<ObjectPool<T>> {
public:
using InitFunc = std::function<void(T *)>;
using ObjectPoolPtr = std::shared_ptr<ObjectPool<T>>;
template <typename... Args>
explicit ObjectPool(uint32_t num_objects, Args &&... args);
template <typename... Args>
ObjectPool(uint32_t num_objects, InitFunc f, Args &&... args);
virtual ~ObjectPool();
std::shared_ptr<T> GetObject();
private:
struct Node {
T object;
Node *next;
};
ObjectPool(ObjectPool &) = delete;
ObjectPool &operator=(ObjectPool &) = delete;
void ReleaseObject(T *);
uint32_t num_objects_ = 0;
char *object_arena_ = nullptr;
Node *free_head_ = nullptr;
};
该类使用内存池技术,预先分配num_objects_个对象,通过链表管理空闲对象。GetObject返回shared_ptr,当shared_ptr析构时自动调用ReleaseObject归还对象到池中。
6.4.2 构造函数
ObjectPool构造函数分配内存并初始化对象。
template <typename T>
template <typename... Args>
ObjectPool<T>::ObjectPool(uint32_t num_objects, Args &&... args)
: num_objects_(num_objects) {
const size_t size = sizeof(Node);
object_arena_ = static_cast<char *>(std::calloc(num_objects_, size));
if (object_arena_ == nullptr) {
throw std::bad_alloc();
}
FOR_EACH(i, 0, num_objects_) {
T *obj = new (object_arena_ + i * size) T(std::forward<Args>(args)...);
reinterpret_cast<Node *>(obj)->next = free_head_;
free_head_ = reinterpret_cast<Node *>(obj);
}
}
构造函数使用calloc分配连续内存,然后使用placement new构造每个对象。每个对象被添加到空闲链表中,free_head_指向链表头部。如果内存分配失败,抛出std::bad_alloc异常。
6.4.3 GetObject方法
GetObject方法从对象池获取对象。
template <typename T>
std::shared_ptr<T> ObjectPool<T>::GetObject() {
if (cyber_unlikely(free_head_ == nullptr)) {
return nullptr;
}
auto self = this->shared_from_this();
auto obj =
std::shared_ptr<T>(reinterpret_cast<T *>(free_head_),
[self](T *object) { self->ReleaseObject(object); });
free_head_ = free_head_->next;
return obj;
}
GetObject方法从空闲链表头部取出对象,创建shared_ptr并设置自定义删除器。删除器调用ReleaseObject将对象归还到池中。如果池为空,返回nullptr。
6.4.4 ReleaseObject方法
ReleaseObject方法将对象归还到对象池。
template <typename T>
void ObjectPool<T>::ReleaseObject(T *object) {
if (cyber_unlikely(object == nullptr)) {
return;
}
reinterpret_cast<Node *>(object)->next = free_head_;
free_head_ = reinterpret_cast<Node *>(object);
}
ReleaseObject方法将对象添加到空闲链表头部,使其可以被后续的GetObject调用重用。该方法不需要加锁,因为假设GetObject和ReleaseObject在同一个线程中调用,或者使用外部同步。
6.5 ThreadPool分析
6.5.1 类定义
ThreadPool是一个线程池实现,使用BoundedQueue管理任务队列。
class ThreadPool {
public:
explicit ThreadPool(std::size_t thread_num, std::size_t max_task_num = 1000);
template <typename F, typename... Args>
auto Enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>;
~ThreadPool();
private:
std::vector<std::thread> workers_;
BoundedQueue<std::function<void()>> task_queue_;
std::atomic_bool stop_;
};
该类使用vector存储工作线程,BoundedQueue存储任务,atomic_bool控制线程池停止。Enqueue方法支持任意可调用对象,返回future用于获取结果。
6.5.2 构造函数
ThreadPool构造函数创建工作线程并启动。
inline ThreadPool::ThreadPool(std::size_t threads, std::size_t max_task_num)
: stop_(false) {
if (!task_queue_.Init(max_task_num, new BlockWaitStrategy())) {
throw std::runtime_error("Task queue init failed.");
}
workers_.reserve(threads);
for (size_t i = 0; i < threads; ++i) {
workers_.emplace_back([this] {
while (!stop_) {
std::function<void()> task;
if (task_queue_.WaitDequeue(&task)) {
task();
}
}
});
}
}
构造函数初始化任务队列,使用BlockWaitStrategy等待任务。然后创建threads个工作线程,每个线程循环从队列中取出任务并执行。stop_标志控制线程退出。
6.5.3 Enqueue方法
Enqueue方法提交任务到线程池。
template <typename F, typename... Args>
auto ThreadPool::Enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::future<return_type> res = task->get_future();
// don't allow enqueueing after stopping the pool
if (stop_) {
return std::future<return_type>();
}
task_queue_.Enqueue([task]() { (*task)(); });
return res;
};
Enqueue方法使用packaged_task包装可调用对象,获取future用于获取结果。然后将packaged_task包装为无参数函数并入队。如果线程池已停止,返回无效的future。
6.5.4 析构函数
~ThreadPool析构函数停止线程池并等待所有线程退出。
inline ThreadPool::~ThreadPool() {
if (stop_.exchange(true)) {
return;
}
task_queue_.BreakAllWait();
for (std::thread& worker : workers_) {
worker.join();
}
}
析构函数首先设置stop_标志,然后中断任务队列的所有等待操作,最后等待所有工作线程退出。exchange操作确保只执行一次停止操作。
6.6 WaitStrategy分析
6.6.1 WaitStrategy接口
WaitStrategy是等待策略的抽象接口。
class WaitStrategy {
public:
virtual ~WaitStrategy() {}
virtual void NotifyOne() = 0;
virtual void BreakAllWait() = 0;
virtual bool EmptyWait() = 0;
};
该接口定义了三个纯虚函数:NotifyOne通知一个等待的线程,BreakAllWait中断所有等待,EmptyWait在队列为空时等待。
6.6.2 BlockWaitStrategy
BlockWaitStrategy使用条件变量实现阻塞等待。
class BlockWaitStrategy : public WaitStrategy {
public:
BlockWaitStrategy() {}
~BlockWaitStrategy() {}
void NotifyOne() override {
std::lock_guard<std::mutex> lock(mutex_);
cond_.notify_one();
}
void BreakAllWait() override {
std::lock_guard<std::mutex> lock(mutex_);
break_all_wait_ = true;
cond_.notify_all();
}
bool EmptyWait() override {
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this] { return break_all_wait_; });
return !break_all_wait_;
}
private:
std::mutex mutex_;
std::condition_variable cond_;
bool break_all_wait_ = false;
};
BlockWaitStrategy使用mutex和condition_variable实现阻塞等待。NotifyOne通知一个等待的线程,BreakAllWait设置标志并通知所有线程,EmptyWait等待条件变量。
6.6.3 SleepWaitStrategy
SleepWaitStrategy使用睡眠实现等待。
class SleepWaitStrategy : public WaitStrategy {
public:
SleepWaitStrategy() {}
~SleepWaitStrategy() {}
void NotifyOne() override {}
void BreakAllWait() override { break_all_wait_ = true; }
bool EmptyWait() override {
if (break_all_wait_) {
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
return true;
}
private:
std::atomic<bool> break_all_wait_{false};
};
SleepWaitStrategy使用睡眠1毫秒实现等待,NotifyOne不做任何操作,BreakAllWait设置原子标志。该策略适用于低延迟场景,避免了条件变量的开销。
6.7 Macros分析
6.7.1 缓存行对齐
CACHELINE_SIZE宏定义了缓存行大小。
#define CACHELINE_SIZE 64
该宏用于alignas指令,确保变量位于不同的缓存行,避免伪共享。现代CPU的缓存行大小通常为64字节。
6.7.2 分支预测提示
cyber_likely和cyber_unlikely宏提供分支预测提示。
#define cyber_likely(x) __builtin_expect(!!(x), 1)
#define cyber_unlikely(x) __builtin_expect(!!(x), 0)
这些宏使用GCC的__builtin_expect内置函数,提示编译器某个条件更可能为真或假,优化分支预测。
6.8 ForEach分析
6.8.1 FOR_EACH宏
FOR_EACH宏用于循环展开。
#define FOR_EACH(i, start, end) \
for (int i = (start); i < (end); ++i)
该宏简化了循环的编写,常用于对象池的初始化和销毁。虽然简单,但提高了代码的可读性。
7. 设计模式
7.1 对象池模式(Object Pool Pattern)
ObjectPool类实现了对象池模式,预先分配对象并重用它们。
template <typename T>
class ObjectPool : public std::enable_shared_from_this<ObjectPool<T>> {
public:
std::shared_ptr<T> GetObject();
private:
void ReleaseObject(T *object);
uint32_t num_objects_ = 0;
char *object_arena_ = nullptr;
Node *free_head_ = nullptr;
};
该模式通过预先分配对象池,减少了频繁的内存分配和释放开销。GetObject从池中获取对象,ReleaseObject将对象归还到池中。shared_ptr的自定义删除器确保对象自动归还。
7.2 策略模式(Strategy Pattern)
WaitStrategy接口实现了策略模式,支持不同的等待策略。
class WaitStrategy {
public:
virtual void NotifyOne() = 0;
virtual void BreakAllWait() = 0;
virtual bool EmptyWait() = 0;
};
class BlockWaitStrategy : public WaitStrategy { ... };
class SleepWaitStrategy : public WaitStrategy { ... };
class YieldWaitStrategy : public WaitStrategy { ... };
class SpinWaitStrategy : public WaitStrategy { ... };
该模式允许在运行时选择不同的等待策略,如阻塞等待、睡眠等待、让出等待或自旋等待。不同的策略适用于不同的场景,如高吞吐量或低延迟。
7.3 RAII模式(RAII Pattern)
ReadLockGuard和WriteLockGuard类实现了RAII模式,自动管理锁的生命周期。
template <typename Lock>
class ReadLockGuard {
public:
explicit ReadLockGuard(Lock& lock) : lock_(lock) {
lock_.ReadLock();
}
~ReadLockGuard() { lock_.ReadUnlock(); }
private:
Lock& lock_;
};
该模式通过构造函数获取锁,析构函数释放锁,确保锁的正确释放,避免死锁。即使发生异常,锁也会被正确释放。
7.4 工厂模式(Factory Pattern)
ThreadPool::Enqueue方法实现了工厂模式,创建packaged_task对象。
template <typename F, typename... Args>
auto ThreadPool::Enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::future<return_type> res = task->get_future();
task_queue_.Enqueue([task]() { (*task)(); });
return res;
};
该方法根据可调用对象的类型创建相应的packaged_task,并返回future用于获取结果。这种工厂模式简化了任务的创建和管理。
7.5 单例模式(Singleton Pattern)
虽然base模块中没有显式的单例模式,但ObjectPool通常作为单例使用,通过shared_ptr管理生命周期。
auto pool = std::make_shared<ObjectPool<MyClass>>(100);
auto obj = pool->GetObject();
这种模式确保对象池只有一个实例,所有代码共享同一个池,提高了资源利用率。
7.6 观察者模式(Observer Pattern)
WaitStrategy接口实现了观察者模式,NotifyOne通知等待的线程。
class BlockWaitStrategy : public WaitStrategy {
public:
void NotifyOne() override {
std::lock_guard<std::mutex> lock(mutex_);
cond_.notify_one();
}
};
该模式中,等待的线程是观察者,WaitStrategy是被观察者。当有新任务时,NotifyOne通知一个观察者。
8. 总结
apollo_cyber_base子模块通过精心设计的无锁数据结构和同步原语,为Cyber RT中间件提供了高性能的并发编程基础。其核心优势在于原子操作的广泛应用、缓存行对齐优化、多种等待策略支持以及RAII模式的锁管理。通过对象池模式、策略模式、RAII模式等设计模式的应用,该模块实现了资源的高效利用和代码的简洁性。模块的无锁哈希表、原子读写锁、有界队列等组件在保证线程安全的同时,显著提升了并发性能,为自动驾驶系统的实时通信和任务调度提供了坚实的技术基础。