class __pool<true>
__pool<true>是多线程下的内存池类型。在某些方面,它是__pool<false>的扩展,但是在其他很多方面,它比__pool<false>要复杂得多。__pool<true>的基类也是__pool_base,所以它继承了__pool_base的所有成员变量,包括_M_options,_M_binmap和_M_init。同时__pool_base::_Tune和__pool_base::_Block_address类型也是从__pool_base继承而来。
257 #ifdef __GTHREADS
所有__pool<true>的代码都包裹在这个条件宏里,所以如果你的程序没有支持多线程,__pool<true>就不会存在。
258 /// Specialization for thread enabled, via gthreads.h.
259 template<>
260 class __pool<true> : public __pool_base
261 {
262 public:
263 // Each requesting thread is assigned an id ranging from 1 to
264 // _S_max_threads. Thread id 0 is used as a global memory pool.
265 // In order to get constant performance on the thread assignment
266 // routine, we keep a list of free ids. When a thread first
267 // requests memory we remove the first record in this list and
268 // stores the address in a __gthread_key. When initializing the
269 // __gthread_key we specify a destructor. When this destructor
270 // (i.e. the thread dies) is called, we return the thread id to
271 // the front of this list.
这段注释描述了_Thread_record的作用:给线程分配id。详细的内容会在后面研究到。
272 struct _Thread_record
273 {
274 // Points to next free thread id record. NULL if last record in list.
275 _Thread_record* volatile _M_next;
276
277 // Thread id ranging from 1 to _S_max_threads.
278 size_t _M_id;
279 };
__pool<true>里存在一个由_Thread_record对象组成的链表,它们的_M_id成员会分别初始化为1,2,...,_S_max_threads,所以每个_Thread_record对象代表了一个线程id。当某线程第一次向__mt_alloc申请内存时,__pool<true>从这个链表里拿出一个_Thread_record对象,分配给该线程,于是这个线程的id就是_Thread_record对象的_M_id成员值。当这个线程退出的时候,__pool<true>会回收_Thread_record对象,这样就可以把这个id重新分配给其他线程。
281 union _Block_record
282 {
283 // Points to the block_record of the next free block.
284 _Block_record* volatile _M_next;
285
286 // The thread id of the thread which has requested this block.
287 size_t _M_thread_id;
288 };
前面研究过,联合结构体_Block_record是空闲内存块的头信息。对比一下__pool<false>::_Block_record的定义,可以发现这里多了一个_M_thread_id,其实它才是_Block_record真正需要“保存”的信息。分配给用户使用的内存块,会把执行“分配”操作的线程的id存放在_M_thread_id里,由于内存块可能在线程之间“传递”,最后执行“释放”操作的不一定是原来的那个线程,所以只能通过_M_thread_id来知道这些信息。
290 struct _Bin_record
291 {
292 // An "array" of pointers to the first free block for each
293 // thread id. Memory to this "array" is allocated in
294 // _S_initialize() for _S_max_threads + global pool 0.
295 _Block_record** volatile _M_first;
296
297 // A list of the initial addresses of all allocated blocks.
298 _Block_address* _M_address;
这2个成员变量的意义和__pool<false>::_Bin_record里的是一样的。
300 // An "array" of counters used to keep track of the amount of
301 // blocks that are on the freelist/used for each thread id.
302 // Memory to these "arrays" is allocated in _S_initialize() for
303 // _S_max_threads + global pool 0.
304 size_t* volatile _M_free;
305 size_t* volatile _M_used;
这是2个计数器数组,分别记录每个线程id对应的空闲内存块和正在使用内存块的个数。也许读者还记得,__mt_alloc会在一定条件下把某个线程的空闲内存块归还给全局的空闲块链表,这个条件就是由线程的used和free计数器决定的。
307 // Each bin has its own mutex which is used to ensure data
308 // integrity while changing "ownership" on a block. The mutex
309 // is initialized in _S_initialize().
310 __gthread_mutex_t* _M_mutex;
每当涉及到多线程间操作bin的内部成员时,都会进行加锁与解锁操作。
311 };
__pool<true>成员变量
__pool<true>有一部分成员变量是从基类__pool_base继承而来,在研究__pool<false>的时候已经介绍过它们了。这里主要看看__pool<true>自己的成员变量。
366 // An "array" of bin_records each of which represents a specific
367 // power of 2 size. Memory to this "array" is allocated in
368 // _M_initialize().
369 _Bin_record* volatile _M_bin;
370
371 // Actual value calculated in _M_initialize().
372 size_t _M_bin_size;
这2个成员的意义和__pool<true>里的一样,无需介绍。
374 _Thread_record* _M_thread_freelist;
375 void* _M_thread_freelist_initial;
_M_thread_freelist是前面说过的未分配线程id的链表。_M_thread_freelist_initial的作用不大清楚,因为没找到使用它的地方。