Untitled
unknown
plain_text
a year ago
22 kB
3
Indexable
Never
// 关键看懂 这个链接中的一张图 就大概掌握了 整个代码 https://blog.csdn.net/KIDGIN7439/article/details/120219052 #include <algorithm> // std::max, std::min #include <iostream> // std::ostream #include <pthread.h> // pthread_mutex_t #include <atomic> #include <vector> #include <mutex> #include <string.h> #define BAIDU_LIKELY(expr) (expr) #define BAIDU_UNLIKELY(expr) (expr) #define BAIDU_CACHELINE_SIZE 64 #define BAIDU_CACHELINE_ALIGNMENT __attribute__((aligned(BAIDU_CACHELINE_SIZE))) namespace butil { template <typename T, size_t NITEM> struct ObjectPoolFreeChunk { size_t nfree; // 表示可用对象的数量。 T *ptrs[NITEM]; // 一个大小为 NITEM 的数组,存储指向类型 T 的对象的指针。 }; // for gcc 3.4.5 template <typename T> struct ObjectPoolFreeChunk<T, 0> { // 在这个特化版本中,NITEM 为 0,因此 ptrs 数组的大小为 0。这个特化版本是为了解决某些编译器在处理大小为 0 的数组时可能出现的问题。 size_t nfree; T *ptrs[0]; }; template <typename T> struct ObjectPoolFreeChunkMaxItem { static size_t value() { return 256; } }; struct ObjectPoolInfo { size_t local_pool_num; size_t block_group_num; size_t block_num; size_t item_num; size_t block_item_num; size_t free_chunk_item_num; size_t total_size; }; static const size_t OP_MAX_BLOCK_NGROUP = 65536; static const size_t OP_GROUP_NBLOCK_NBIT = 16; static const size_t OP_GROUP_NBLOCK = (1UL << OP_GROUP_NBLOCK_NBIT); // 65536 static const size_t OP_INITIAL_FREE_LIST_SIZE = 1024; template <typename T> struct ObjectPoolBlockMaxSize { // 定义了每个块的最大字节大小,这里设置为 64 * 1024 字节(即 64KB) static const size_t value = 64 * 1024; // bytes }; template <typename T> struct ObjectPoolBlockMaxItem { // 定义了每个块中最多可以容纳的项目数量,这里设置为 256 个 static const size_t value = 256; }; template <typename T> class ObjectPoolBlockItemNum { // 计算类,用于确定每个块实际包含的项目数量。它根据以下步骤计算: static const size_t N1 = ObjectPoolBlockMaxSize<T>::value / sizeof(T); // 这个值 表示块的最大字节大小可以容纳多少个对象。 static const size_t N2 = (N1 < 1 ? 1 : N1); // 这样可以确保每个块至少包含一个对象。 public: static const size_t value = (N2 > ObjectPoolBlockMaxItem<T>::value ? ObjectPoolBlockMaxItem<T>::value : N2); // 如果 N2 大于最大项目数,则将 value 设置为最大项目数,否则将 value 设置为 N2。这个值表示每个块实际应包含的项目数量。 }; template <typename T> class BAIDU_CACHELINE_ALIGNMENT ObjectPool { public: static const size_t BLOCK_NITEM = ObjectPoolBlockItemNum<T>::value; // 这个值表示每个块实际应包含的项目数量。 static const size_t FREE_CHUNK_NITEM = BLOCK_NITEM; // 256 typedef ObjectPoolFreeChunk<T, FREE_CHUNK_NITEM> FreeChunk; typedef ObjectPoolFreeChunk<T, 0> DynamicFreeChunk; struct BAIDU_CACHELINE_ALIGNMENT Block { char items[sizeof(T) * BLOCK_NITEM]; // 每个Block 都是一块连续内存 256*T 个这样的字节 就是 256个T size_t nitem; Block() : nitem(0) {} }; struct BlockGroup { std::atomic<size_t> nblock; std::atomic<Block *> blocks[OP_GROUP_NBLOCK]; BlockGroup() : nblock(0) { memset(static_cast<void *>(blocks), 0, sizeof(std::atomic<Block *>) * OP_GROUP_NBLOCK); } }; class BAIDU_CACHELINE_ALIGNMENT LocalPool { public: explicit LocalPool(ObjectPool *pool) : _pool(pool), _cur_block(NULL), _cur_block_index(0) { _cur_free.nfree = 0; } ~LocalPool() { // Add to global _free if there're some free objects if (_cur_free.nfree) { _pool->push_free_chunk(_cur_free); } //_pool->clear_from_destructor_of_local_pool(); } static void delete_local_pool(void *arg) { delete (LocalPool *)arg; } #define BAIDU_OBJECT_POOL_GET(CTOR_ARGS) \ if (_cur_free.nfree) \ { \ return _cur_free.ptrs[--_cur_free.nfree]; \ } \ if (_pool->pop_free_chunk(_cur_free)) \ { \ return _cur_free.ptrs[--_cur_free.nfree]; \ } \ if (_cur_block && _cur_block->nitem < BLOCK_NITEM) \ { \ T *obj = new ((T *)_cur_block->items + _cur_block->nitem) T CTOR_ARGS; \ ++_cur_block->nitem; \ return obj; \ } \ /*去 _cur_block 中看有没有空闲,如果有则直接placement new即可,因为初始化的时候 _cur_block 是null,因此也拿不到 \ 此时就会去全局新建一个Block赋给 _cur_block 然后从cur_block里创建TestClass返回*/ \ _cur_block = add_block(&_cur_block_index); \ if (_cur_block != NULL) \ { \ T *obj = new ((T *)_cur_block->items + _cur_block->nitem) T CTOR_ARGS; \ ++_cur_block->nitem; \ return obj; \ } \ return NULL; inline T *get() { BAIDU_OBJECT_POOL_GET(); } template <typename A1> inline T *get(const A1 &a1) { BAIDU_OBJECT_POOL_GET((a1)); } template <typename A1, typename A2> inline T *get(const A1 &a1, const A2 &a2) { BAIDU_OBJECT_POOL_GET((a1, a2)); } #undef BAIDU_OBJECT_POOL_GET void clear_objects() { LocalPool *lp = _local_pool; if (lp) { _local_pool = NULL; pthread_key_create(&key, LocalPool::delete_local_pool); // butil::thread_atexit_cancel(LocalPool::delete_local_pool, lp); delete lp; } } inline int return_object(T *ptr) { // Return to local free list if (_cur_free.nfree < ObjectPool::free_chunk_nitem()) { _cur_free.ptrs[_cur_free.nfree++] = ptr; return 0; } // Local free list is full, return it to global. // For copying issue, check comment in upper get() if (_pool->push_free_chunk(_cur_free)) { _cur_free.nfree = 1; _cur_free.ptrs[0] = ptr; return 0; } return -1; } public: ObjectPool *_pool; Block *_cur_block; size_t _cur_block_index; FreeChunk _cur_free; pthread_key_t key; }; // Create a Block and append it to right-most BlockGroup. static Block *add_block(size_t *index) { Block *const new_block = new (std::nothrow) Block; // 首先,尝试创建一个新的 Block 对象,将其地址赋值给 new_block 如果创建失败(内存分配失败),则返回 NULL。 if (NULL == new_block) { return NULL; } size_t ngroup; // 第一次的时候 _ngroup == 0 ; do { ngroup = _ngroup.load(std::memory_order_acquire); // a. 从 _ngroup 原子变量中加载当前的 block 组数量 ngroup,使用 std::memory_order_acquire 内存顺序模型。 if (ngroup >= 1) { // 如果 ngroup 大于等于 1,获取最后一个 block 组 g。 BlockGroup *const g = _block_groups[ngroup - 1].load(std::memory_order_consume); // 获取 add_block_group 创建的 BlockGroup const size_t block_index = g->nblock.fetch_add(1, std::memory_order_relaxed); // 使用 fetch_add 函数为当前 block 组的 nblock 原子变量增加 1,并将结果存储在 block_index 中。使用 std::memory_order_relaxed 内存顺序模型。 if (block_index < OP_GROUP_NBLOCK) { // 如果 block_index 小于 OP_GROUP_NBLOCK (表示当前 block 组中还有可用空间),将新创建的 new_block 存储到 block 组的 blocks[block_index] 中,使用 std::memory_order_release 内存顺序模型。然后,计算新 Block 的全局索引,并将其存储在 *index 中,最后返回新创建的 Block。 g->blocks[block_index].store( new_block, std::memory_order_release); *index = (ngroup - 1) * OP_GROUP_NBLOCK + block_index; // 计算新 Block 的全局索引,并将其存储在 *index 中 return new_block; } g->nblock.fetch_sub(1, std::memory_order_relaxed); // 如果 block_index 不满足条件(即当前 block 组已满),使用 fetch_sub 函数将 block 组的 nblock 原子变量减 1,使用 std::memory_order_relaxed 内存顺序模型。 } } while (add_block_group(ngroup)); // ngroup == 0 会走这里 // Fail to add_block_group. delete new_block; return NULL; } // Create a BlockGroup and append it to _block_groups. // Shall be called infrequently because a BlockGroup is pretty big. static bool add_block_group(size_t old_ngroup) { // 这里就是会 new 一个 BlockGroup _ngroup 也会加 1 BlockGroup *bg = NULL; // BAIDU_SCOPED_LOCK(_block_group_mutex); std::lock_guard<std::mutex> scoped_locker_dummy_at_line_43(_block_group_mutex); const size_t ngroup = _ngroup.load(std::memory_order_acquire); if (ngroup != old_ngroup) { // Other thread got lock and added group before this thread. return true; } if (ngroup < OP_MAX_BLOCK_NGROUP) { bg = new (std::nothrow) BlockGroup; if (NULL != bg) { // Release fence is paired with consume fence in add_block() // to avoid un-constructed bg to be seen by other threads. _block_groups[ngroup].store(bg, std::memory_order_release); _ngroup.store(ngroup + 1, std::memory_order_release); } } return bg != NULL; } inline LocalPool *get_or_new_local_pool() { // 通过这个函数,可以确保每个线程都有一个与之关联的 LocalPool 实例,并且在线程退出时正确地删除这些实例。 LocalPool *lp = _local_pool; if (BAIDU_LIKELY(lp != NULL)) { return lp; } lp = new (std::nothrow) LocalPool(this); if (NULL == lp) { return NULL; } // BAIDU_SCOPED_LOCK(_change_thread_mutex); //avoid race with clear() // 替换成下面的容易理解 std::lock_guard<std::mutex> scoped_locker_dummy_at_line_42( _change_thread_mutex); _local_pool = lp; // butil::thread_atexit(LocalPool::delete_local_pool, lp);//线程退出时候 处理的一些任务 pthrea的库中有类似的函数 以便在当前线程退出时自动删除关联的 LocalPool 实例 // 这两个pthread 代替上面 thread_atexit pthread_setspecific(lp->key, lp); // 在程序的某个地方初始化键,并提供清理函数。 pthread_key_create(&(lp->key), LocalPool::delete_local_pool); //_nlocal.fetch_add(1, butil::memory_order_relaxed);//使用 fetch_add 函数原子地递增 _nlocal 成员变量的值。butil::memory_order_relaxed 表示使用松散的内存顺序,这意味着在递增 _nlocal 时不需要保证其他内存操作的顺序。 _nlocal.fetch_add(1, std::memory_order_relaxed); return lp; } inline T *get_object() { LocalPool *lp = get_or_new_local_pool(); if (BAIDU_LIKELY(lp != NULL)) { return lp->get(); } return NULL; } inline int return_object(T *ptr) { LocalPool *lp = get_or_new_local_pool(); if (BAIDU_LIKELY(lp != NULL)) { return lp->return_object(ptr); } return -1; } static inline ObjectPool *singleton() { ObjectPool *p = _singleton.load(std::memory_order_consume); if (p) { return p; } pthread_mutex_lock(&_singleton_mutex); p = _singleton.load(std::memory_order_consume); if (!p) { p = new ObjectPool(); _singleton.store(p, std::memory_order_release); } pthread_mutex_unlock(&_singleton_mutex); return p; } inline static size_t free_chunk_nitem() { const size_t n = ObjectPoolFreeChunkMaxItem<T>::value(); return (n < FREE_CHUNK_NITEM ? n : FREE_CHUNK_NITEM); } private: ObjectPool() { _free_chunks.reserve(OP_INITIAL_FREE_LIST_SIZE); // 1024 pthread_mutex_init(&_free_chunks_mutex, NULL); } ~ObjectPool() { pthread_mutex_destroy(&_free_chunks_mutex); } bool pop_free_chunk(FreeChunk &c) { // free_chunks 初始化时是空,只reserve了一个空间 所以 pop_free_chunk 也会失败,当不为空时候,会拿到一个 free_chunk 然后拷贝到LocalPool的cur_free中 // Critical for the case that most return_object are called in // different threads of get_object. if (_free_chunks.empty()) { return false; } pthread_mutex_lock(&_free_chunks_mutex); if (_free_chunks.empty()) { pthread_mutex_unlock(&_free_chunks_mutex); return false; } DynamicFreeChunk *p = _free_chunks.back(); _free_chunks.pop_back(); pthread_mutex_unlock(&_free_chunks_mutex); c.nfree = p->nfree; memcpy(c.ptrs, p->ptrs, sizeof(*p->ptrs) * p->nfree); // 当不为空时候,会拿到一个 free_chunk 然后拷贝到LocalPool的 _cur_free 中 free(p); return true; } bool push_free_chunk(const FreeChunk &c) { DynamicFreeChunk *p = (DynamicFreeChunk *)malloc( offsetof(DynamicFreeChunk, ptrs) + sizeof(*c.ptrs) * c.nfree); if (!p) { return false; } p->nfree = c.nfree; memcpy(p->ptrs, c.ptrs, sizeof(*c.ptrs) * c.nfree); pthread_mutex_lock(&_free_chunks_mutex); _free_chunks.push_back(p); pthread_mutex_unlock(&_free_chunks_mutex); return true; } static std::atomic<ObjectPool *> _singleton; static pthread_mutex_t _singleton_mutex; static std::atomic<long> _nlocal; static __thread LocalPool *_local_pool; static std::atomic<size_t> _ngroup; static std::mutex _change_thread_mutex; static std::mutex _block_group_mutex; static std::atomic<BlockGroup *> _block_groups[OP_MAX_BLOCK_NGROUP]; // OP_MAX_BLOCK_NGROUP == 65535 std::vector<DynamicFreeChunk *> _free_chunks; // 一个存储空闲对象块的向量 其元素类型为指向 DynamicFreeChunk 类型的指针。这个向量用于存储空闲的动态内存块,即不再使用的 DynamicFreeChunk 对象。这样在需要新的空闲块时,可以从这个向量中获取,而不是分配新的内存。同样,在释放内存块时,可以将它们添加回这个向量,以便将来重用。这有助于减少内存分配和释放的开销。 pthread_mutex_t _free_chunks_mutex; // 互斥锁,用于保护 _free_chunks 的访问 }; template <typename T> __thread typename ObjectPool<T>::LocalPool * ObjectPool<T>::_local_pool = NULL; template <typename T> std::atomic<ObjectPool<T> *> ObjectPool<T>::_singleton = NULL; template <typename T> std::atomic<long> ObjectPool<T>::_nlocal(0); template <typename T> std::atomic<size_t> ObjectPool<T>::_ngroup(0); template <typename T> pthread_mutex_t ObjectPool<T>::_singleton_mutex = PTHREAD_MUTEX_INITIALIZER; template <typename T> std::mutex ObjectPool<T>::_block_group_mutex; template <typename T> std::mutex ObjectPool<T>::_change_thread_mutex; template <typename T> std::atomic<typename ObjectPool<T>::BlockGroup *> ObjectPool<T>::_block_groups[OP_MAX_BLOCK_NGROUP] = {}; } // namespace butil namespace butil { template <typename T> inline T *get_object() { return ObjectPool<T>::singleton()->get_object(); } template <typename T, typename A1> inline T *get_object(const A1 &arg1) { return ObjectPool<T>::singleton()->get_object(arg1); } template <typename T, typename A1, typename A2> inline T *get_object(const A1 &arg1, const A2 &arg2) { return ObjectPool<T>::singleton()->get_object(arg1, arg2); } template <typename T> inline int return_object(T *ptr) { return ObjectPool<T>::singleton()->return_object(ptr); } template <typename T> inline void clear_objects() { ObjectPool<T>::singleton()->clear_objects(); } /* template <typename T> ObjectPoolInfo describe_objects() { return ObjectPool<T>::singleton()->describe_objects(); }*/ } struct MyObject { int num; }; int main() { MyObject *p = butil::get_object<MyObject>(); p->num = 100; std::cout << p->num << std::endl; butil::return_object(p); std::cout << p->num << std::endl; return 0; }