1、ConcurrentQueue构造
template <typename T, typename Traits = ConcurrentQueueDefaultTraits>
class ConcurrentQueue;
// 三种构造函数:两个有参一个移动构造,禁止拷贝构造和赋值
// 有参构造函数的步骤大同小异,都是对哈希和链表(变量名叫list,但是是连续的内存)初始化,哈希的每个key在
// populate_initial_implicit_producer_hash()函数内都初始化为0
// 在populate_initial_block_list中先申请堆内存,然后再用placement new对每个内存进行初始化
explicit ConcurrentQueue(size_t capacity = 32 * BLOCK_SIZE)
: producerListTail(nullptr),
producerCount(0),
initialBlockPoolIndex(0),
nextExplicitConsumerId(0),
globalExplicitConsumerOffset(0)
{
implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
populate_initial_implicit_producer_hash();
populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1));
}
//
ConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
: producerListTail(nullptr),
producerCount(0),
initialBlockPoolIndex(0),
nextExplicitConsumerId(0),
globalExplicitConsumerOffset(0)
{
implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
populate_initial_implicit_producer_hash();
size_t blocks = (((minCapacity + BLOCK_SIZE - 1) / BLOCK_SIZE) - 1) * (maxExplicitProducers + 1) + 2 * (maxExplicitProducers + maxImplicitProducers);
populate_initial_block_list(blocks);
}
2、enqueue压入数据
// 压入大同小异,只分析一种
inline bool enqueue(T const &item)
{
MOODYCAMEL_CONSTEXPR_IF(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0)
return false; // 直接看inner_enqueue()
else return inner_enqueue<CanAlloc>(item);
}
inline bool inner_enqueue(U &&element)
{
auto producer = get_or_add_implicit_producer();
return producer == nullptr ? false :
producer->ConcurrentQueue::ImplicitProducer::template enqueue<canAlloc>(std::forward<U>(element));
// producer是一个容器,真正将元素压到队列中是enqueue函数
}
// 压入的重点在get_or_add_implicit_producer()
// 获取值存放的内存
ImplicitProducer *get_or_add_implicit_producer()
{
auto id = details::thread_id(); // 固定值,同一个线程id相同
auto hashedId = details::hash_thread_id(id); // 经过计算后的hash值也相同
/*
implicitProducerHash是一个原子对象,存放的是ImplicitProducerHash的地址值
ImplicitProducerHash是一个结构体,成员prev指向下一个ImplicitProducerHash
每个ImplicitProducerHash都有一个hash数组,
一下就是遍历链表每个节点,找到与id相同的,然后再将value存放到哈希表,
如果已经存满会跳出循环,然后对哈希表进行扩容,每次扩容会将容量乘2
*/
auto mainHash = implicitProducerHash.load(std::memory_order_acquire);
assert(mainHash != nullptr); // silence clang-tidy and MSVC warnings (hash cannot be null)
for (auto hash = mainHash; hash != nullptr; hash = hash->prev)
{
// Look for the id in this hash
auto
并发队列实现与优化

本文详细介绍了并发队列ConcurrentQueue的构造过程,包括两种构造函数的实现,以及如何通过哈希表和链表优化线程安全的入队和出队操作。在入队操作中,通过隐式生产者哈希表进行高效查找和内存管理,避免了不必要的锁竞争。出队操作采用启发式策略选择最佳的生产者,以降低等待时间。这些设计确保了在高并发场景下,ConcurrentQueue能提供高效稳定的服务。
最低0.47元/天 解锁文章
1648

被折叠的 条评论
为什么被折叠?



