
概述
freeswitch的核心源代碼是基于apr庫開發的,在不同的系統上有很好的移植性,
APR庫在之前的文章中已經介紹過了,APR-UTIL庫是和APR并列的工具庫,它們都是由APACHE開源出來的跨平臺可移植庫,不同點在于庫中實作的功能介面有區別,
在應用的開發程序中,多執行緒并發是提高效率的常用方案,但是多執行緒管理并不好做,
在很多大型應用中,都會引入執行緒池的框架,執行緒池是一個執行緒集合,有統一的管理,當有一個新的任務下發,執行緒池管理會按照一定的策略將任務分配給空閑的執行緒,當任務積壓較多時,執行緒池會創建新的執行緒來加快處理效率,
APR-UTIL庫中就提供了一套執行緒池介面,
我對幾個問題比較好奇,執行緒池如何管理?執行緒池什么情況要增加執行緒?什么情況會減少執行緒?執行緒池執行緒數目如何設定才有最優的效率?
下面我們對apr-util庫的執行緒池實作做一個介紹,
環境
centos:CentOS release 7.0 (Final)或以上版本
APR-UTIL:1.6.1
GCC:4.8.5
本來要使用freeswitch1.8.7中帶的apr-util庫源代碼來梳理,但是很遺憾的是這個apr-util庫版本是1.2.8,里面沒有apr_thread_pool介面,,,所以從APR官網上下載了最新的1.6.1版本來做分析,
資料結構
apr執行緒池源檔案:
apr-util-1.6.1\include\apr_thread_pool.h
apr-util-1.6.1\misc\apr_thread_pool.c
號碼池資料結構定義在apr_thread_pool.c中
typedef struct apr_thread_pool_task
{
APR_RING_ENTRY(apr_thread_pool_task) link;
apr_thread_start_t func;
void *param;
void *owner;
union
{
apr_byte_t priority;
apr_time_t time;
} dispatch;
} apr_thread_pool_task_t;
APR_RING_HEAD(apr_thread_pool_tasks, apr_thread_pool_task);
struct apr_thread_list_elt
{
APR_RING_ENTRY(apr_thread_list_elt) link;
apr_thread_t *thd;
volatile void *current_owner;
volatile enum { TH_RUN, TH_STOP, TH_PROBATION } state;
};
APR_RING_HEAD(apr_thread_list, apr_thread_list_elt);
struct apr_thread_pool
{
apr_pool_t *pool;
volatile apr_size_t thd_max;
volatile apr_size_t idle_max;
volatile apr_interval_time_t idle_wait;
volatile apr_size_t thd_cnt;
volatile apr_size_t idle_cnt;
volatile apr_size_t task_cnt;
volatile apr_size_t scheduled_task_cnt;
volatile apr_size_t threshold;
volatile apr_size_t tasks_run;
volatile apr_size_t tasks_high;
volatile apr_size_t thd_high;
volatile apr_size_t thd_timed_out;
struct apr_thread_pool_tasks *tasks;
struct apr_thread_pool_tasks *scheduled_tasks;
struct apr_thread_list *busy_thds;
struct apr_thread_list *idle_thds;
apr_thread_mutex_t *lock;
apr_thread_cond_t *cond;
volatile int terminated;
struct apr_thread_pool_tasks *recycled_tasks;
struct apr_thread_list *recycled_thds;
apr_thread_pool_task_t *task_idx[TASK_PRIORITY_SEGS];
};
執行緒池記憶體模型總圖,執行緒池,任務佇列,執行緒佇列,

常用函式
常用函式介面
apr_thread_pool_create //Create a thread pool
apr_thread_pool_destroy //Destroy the thread pool and stop all the threads
apr_thread_pool_push //Schedule a task to the bottom of the tasks of same priority.
apr_thread_pool_schedule //Schedule a task to be run after a delay
apr_thread_pool_top //Schedule a task to the top of the tasks of same priority.
apr_thread_pool_tasks_cancel //Cancel tasks submitted by the owner. If there is any task from the owner that is currently running, the function will spin until the task finished.
apr_thread_pool_tasks_count //Get the current number of tasks waiting in the queue
apr_thread_pool_scheduled_tasks_count //Get the current number of scheduled tasks waiting in the queue
apr_thread_pool_threads_count //Get the current number of threads
apr_thread_pool_busy_count //Get the current number of busy threads
apr_thread_pool_idle_count //Get the current number of idle threads
apr_thread_pool_idle_max_set //Access function for the maximum number of idle threads. Number of current idle threads will be reduced to the new limit.
apr_thread_pool_tasks_run_count //Get number of tasks that have run
apr_thread_pool_tasks_high_count //Get high water mark of the number of tasks waiting to run
apr_thread_pool_threads_high_count //Get high water mark of the number of threads
apr_thread_pool_threads_idle_timeout_count //Get the number of idle threads that were destroyed after timing out
apr_thread_pool_idle_max_get //Access function for the maximum number of idle threads
apr_thread_pool_thread_max_set //Access function for the maximum number of threads.
apr_thread_pool_idle_wait_set //Access function for the maximum wait time (in microseconds) of an idling thread that exceeds the maximum number of idling threads. A non-zero value allows for the reaping of idling threads to shrink over time. Which helps reduce thrashing.
apr_thread_pool_idle_wait_get //Access function for the maximum wait time (in microseconds) of an idling thread that exceeds the maximum number of idling threads
apr_thread_pool_thread_max_get //Access function for the maximum number of threads
apr_thread_pool_threshold_set //Access function for the threshold of tasks in queue to trigger a new thread.
apr_thread_pool_threshold_get //Access function for the threshold of tasks in queue to trigger a new thread.
apr_thread_pool_task_owner_get //Get owner of the task currently been executed by the thread.
apr_thread_pool_create創建
APU_DECLARE(apr_status_t) apr_thread_pool_create(apr_thread_pool_t ** me,
apr_size_t init_threads,
apr_size_t max_threads,
apr_pool_t * pool)
介面邏輯:
- 分配一塊大小為apr_thread_pool_t的記憶體tp,
- 在傳入的記憶體池pool中申請一個新的記憶體池tp->pool,
- 初始化執行緒池資料,
a) 執行緒池資料初始化,
b) 創建執行緒互斥鎖me->lock,
c) 創建條件變數me->cond,
d) 在記憶體池pool上分配一塊大小為“apr_thread_pool_tasks“的記憶體賦值給me->tasks,
e) 在記憶體池pool上分配一塊大小為“apr_thread_pool_tasks“的記憶體賦值給me->scheduled_tasks,
f) 在記憶體池pool上分配一塊大小為“apr_thread_pool_tasks“的記憶體賦值給me->recycled_tasks,
g) 在記憶體池pool上分配一塊大小為“apr_thread_list“的記憶體賦值給me->busy_thds,
h) 在記憶體池pool上分配一塊大小為“apr_thread_list“的記憶體賦值給me->idle_thds,
i) 在記憶體池pool上分配一塊大小為“apr_thread_list“的記憶體賦值給me->recycled_thds,
j) 執行緒池資料初始化,
- 在記憶體池tp->pool中注冊清理回呼函式,
- 回圈創建初始作業執行緒,并加入執行緒池的管理,作業執行緒的邏輯見“thread_pool_func作業執行緒”,
- 回傳創建結果,
執行緒池初始化成功后,記憶體模型如圖(作業執行緒啟動未完成時)

thread_pool_func作業執行緒
static void *APR_THREAD_FUNC thread_pool_func(apr_thread_t * t, void *param)
介面邏輯:
- 加鎖me->lock
- 判斷me->recycled_thds鏈表為空?為空則創建新的apr_thread_list_elt節點elt,不為空則獲取recycled_thds中首節點elt并從recycled_thds中移除該節點,
- 回圈處理,
a) 將elt節點加入me->busy_thds鏈表,
b) 獲取一個新任務task,TODO
c) 回圈處理,解鎖me->lock,呼叫任務回呼task->func,加鎖me->lock,將task加入me->recycled_tasks鏈表,獲取新任務task,執行緒狀態置為TH_STOP時跳出回圈,獲取任務為空跳出回圈,
d) 執行緒從busy到stop狀態,將elt加入me->recycled_thds鏈表尾部,解鎖me->lock,退出執行緒,
e) 執行緒從busy到idle狀態,將elt節點從me->busy_thds鏈表中移除,將elt加入me->idle_thds鏈表尾部,
f) 檢查是否有定時任務并獲取任務執行等待時間,
g) 檢查當前空閑執行緒數是否大于最大空閑數,獲取空閑等待時間me->idle_wait,并設定當前執行緒狀態為TH_PROBATION,下一輪回圈中進入stop處理流程,
h) 執行緒阻塞,等待條件變數me->cond的通知或超時,
- 執行緒數me->thd_cnt自減,
- 解鎖me->lock,
- 退出執行緒,
執行緒池初始化成功后,記憶體模型如圖(作業執行緒啟動完成時)

apr_thread_pool_push添加任務
APU_DECLARE(apr_status_t) apr_thread_pool_push(apr_thread_pool_t *me,
apr_thread_start_t func,
void *param,
apr_byte_t priority,
void *owner)
介面邏輯:
- 加鎖me->lock,
- 檢查me->recycled_tasks是否為空,為空則新建任務節點t,不為空則從me->recycled_tasks獲取任務節點t,
- 任務節點t資料初始化,
- 計算任務優先級,根據優先級設定me->task_idx[seg]和me->tasks,
- 當前作業執行緒數為0時,或者空閑執行緒數為0并且當前執行緒數未達到最大并且當前任務數超過閾值等條件,動態創建新的作業執行緒,
- 對條件變數me->cond發通知,
- 解鎖me->lock,
執行緒池添加任務后的記憶體模型圖,

apr_thread_pool_tasks_cancel取消任務
APU_DECLARE(apr_status_t) apr_thread_pool_tasks_cancel(apr_thread_pool_t *me,
void *owner)
介面邏輯:
- 加鎖me->lock,
- 如果當前任務數大于0,則清空owner的所有任務,
- 如果定時任務數大于0,則清空owner的所有定時任務,
- 解鎖me->lock,
- 等待執行緒退出,
總結
APR執行緒池的幾個關注點,
執行緒從busy到stop狀態時,沒有將elt節點從me->busy_thds鏈表中洗掉?
APR執行緒池沒有內置的管理執行緒,根據當前執行緒數和任務數進行動態的調整,而是通過任務閾值、空閑執行緒最大值和超時時間等設定來控制執行緒數的增減,這一點和我開始想的不一樣,
空空如常
求真得真
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/398375.html
標籤:C++
上一篇:力扣刷題筆記 (1)
