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的作用不大清楚,因爲沒找到使用它的地方。