19_apollo_cyber_base子模块软件架构分析
1. 概述
apollo_cyber_base子模块是Apollo Cyber RT中间件的核心基础组件库,提供了高性能并发编程所需的基础数据结构和同步原语。该模块实现了无锁哈希表、原子读写锁、有界队列、对象池、线程池等关键组件,采用现代C++11/14标准和原子操作实现,确保了在高并发场景下的性能和正确性。模块通过精心设计的内存对齐和缓存行优化,减少了伪共享问题,提升了多线程环境下的执行效率,为Cyber RT的通信、调度和任务管理提供了坚实的基础设施支持。
2. 软件架构图
3. 调用流程图
4. UML类图
4.1 核心数据结构类图
4.2 等待策略类图
4.3 读写锁类图
5. 状态机
5.1 BoundedQueue状态机
5.2 AtomicRWLock状态机
5.3 ObjectPool状态机
6. 源码分析
6.1 AtomicHashMap分析
6.1.1 类模板定义
AtomicHashMap是一个无锁固定大小的哈希表实现,使用原子操作确保线程安全。
cpp
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结构体表示哈希表中的一个条目,包含键、值指针和下一个条目的指针。
cpp
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类表示哈希表中的一个桶,使用链表解决哈希冲突。
cpp
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方法在链表中查找指定键,返回前驱节点和目标节点。
cpp
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操作确保线程安全。
cpp
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是一个基于原子操作的读写锁实现。
cpp
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方法获取读锁,支持写优先策略。
cpp
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方法获取写锁,需要等待所有读锁释放。
cpp
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是一个有界队列实现,使用环形缓冲区和原子操作实现无锁并发。
cpp
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方法初始化队列,分配环形缓冲区。
cpp
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操作确保线程安全。
cpp
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操作确保线程安全。
cpp
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方法计算环形缓冲区的索引。
cpp
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是一个对象池实现,用于重用对象减少内存分配开销。
cpp
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构造函数分配内存并初始化对象。
cpp
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方法从对象池获取对象。
cpp
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方法将对象归还到对象池。
cpp
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管理任务队列。
cpp
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构造函数创建工作线程并启动。
cpp
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方法提交任务到线程池。
cpp
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析构函数停止线程池并等待所有线程退出。
cpp
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是等待策略的抽象接口。
cpp
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使用条件变量实现阻塞等待。
cpp
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使用睡眠实现等待。
cpp
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宏定义了缓存行大小。
cpp
#define CACHELINE_SIZE 64
该宏用于alignas指令,确保变量位于不同的缓存行,避免伪共享。现代CPU的缓存行大小通常为64字节。
6.7.2 分支预测提示
cyber_likely和cyber_unlikely宏提供分支预测提示。
cpp
#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宏用于循环展开。
cpp
#define FOR_EACH(i, start, end) \
for (int i = (start); i < (end); ++i)
该宏简化了循环的编写,常用于对象池的初始化和销毁。虽然简单,但提高了代码的可读性。
7. 设计模式
7.1 对象池模式(Object Pool Pattern)
ObjectPool类实现了对象池模式,预先分配对象并重用它们。
cpp
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接口实现了策略模式,支持不同的等待策略。
cpp
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模式,自动管理锁的生命周期。
cpp
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对象。
cpp
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管理生命周期。
cpp
auto pool = std::make_shared<ObjectPool<MyClass>>(100);
auto obj = pool->GetObject();
这种模式确保对象池只有一个实例,所有代码共享同一个池,提高了资源利用率。
7.6 观察者模式(Observer Pattern)
WaitStrategy接口实现了观察者模式,NotifyOne通知等待的线程。
cpp
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模式等设计模式的应用,该模块实现了资源的高效利用和代码的简洁性。模块的无锁哈希表、原子读写锁、有界队列等组件在保证线程安全的同时,显著提升了并发性能,为自动驾驶系统的实时通信和任务调度提供了坚实的技术基础。