物聯網學習教程——執行緒池
執行緒池
執行緒池基本原理
在傳統伺服器結構中,常是有一個總的監聽執行緒監聽有沒有新的使用者連線伺服器,每當有一個新的使用者進入,伺服器就開啟一個新的執行緒使用者處理這 個使用者的資料包。這個執行緒只服務於這個使用者,當使用者與伺服器端關閉連線以後,伺服器端銷燬這個執行緒。
然而頻繁地開闢與銷燬執行緒極大地佔用了系統的資源,而且在大量使用者的情況下,系統為了開闢和銷燬執行緒將浪費大量的時間和資源。執行緒池提供了一個解決外部大量使用者與伺服器有限資源的矛盾。
執行緒池和傳統的一個使用者對應一個執行緒的處理方法不同,它的基本思想就是在程式開始時就在記憶體中開闢一些執行緒,執行緒的數目是固定的,他們獨自形成一個類,遮蔽了對外的操作,而伺服器只需要將資料包交給執行緒池就可以了。當有新的客戶請求到達時,不是新建立一個執行緒為其服務,而是從“池子”中選擇一個空閒的執行緒為新的客戶請求服務,服務完畢後,執行緒進入空閒執行緒池中。如果沒有執行緒空閒的話,就將資料包暫時積累, 等待執行緒池內有執行緒空閒以後再進行處理。透過對多個任務重用已經存在的執行緒物件,降低了對執行緒物件建立和銷燬的開銷。當客戶請求 時,執行緒物件已經存在,可以提高請求的響應時間,從而整體地提高了系統服務的表現。
執行緒池應用例項
一般來說實現一個執行緒池主要包括以下幾個組成部分:
1)執行緒管理器:用於建立並管理執行緒池。
2)工作執行緒:執行緒池中實際執行任務的執行緒。在初始化執行緒時會預先建立好固定數目的執行緒在池中,這些初始化的執行緒一般處於空閒狀態,一般不佔用 CPU,佔用較小的記憶體空間。
3)任務介面:每個任務必須實現的介面,當執行緒池的任務佇列中有可執行任務時,被空閒的工作執行緒調去執行(執行緒的閒與忙是透過互斥量實現的),把任務抽象出來形成介面,可以做到執行緒池與具體的任務無關。
4)任務佇列:用來存放沒有處理的任務,提供一種緩衝機制,實現這種結構有好幾種方法,常用的是佇列,主要運用先進先出原理,另外一種是連結串列之類的資料結構,可以動態的為它分配記憶體空間,應用中比較靈活,此教程就是用到的連結串列。
什麼時候需要建立執行緒池呢?
簡單的說,如果一個應用需要頻繁的建立和銷燬執行緒,而任務執行的時間又非常短,這樣執行緒建立和銷燬的帶來的開銷就不容忽視,這時也是執行緒池該出場的機會了。如果執行緒建立和銷燬時間相比任務執行時間可以忽略不計,則沒有必要使用執行緒池了
執行緒池實現示例程式碼如下:
thread_pool.h 的示例程式碼:
#ifndef __THREAD_POOL_H__
#define __THREAD_POOL_H__
#include <pthread.h>
/*********************************************************************
* 任務回撥函式,也可根據需要自行修改
*********************************************************************/
typedef void *(*pool_task_f)(void *arg);
/*********************************************************************
* 任務控制程式碼
*********************************************************************/
typedef struct _task{
pool_task_f process;/*回撥函式,任務執行時會呼叫此函式,注意也可宣告成其它形式*/
void *arg; /*回撥函式的引數*/
struct _task *next;
}pool_task;
/*********************************************************************
* 執行緒池控制程式碼
*********************************************************************/
typedef struct
{
pthread_t *threadid; /* 執行緒號 */
int threads_limit; /* 執行緒池中允許的活動執行緒數目 */
int destroy_flag; /* 是否銷燬執行緒池 , 0銷燬,1不銷燬*/
pool_task *queue_head; /* 連結串列結構,執行緒池中所有等待任務 */
int task_in_queue; /* 當前等待佇列的任務數目 */
pthread_mutex_t queue_lock; /* 鎖 */
pthread_cond_t queue_ready; /* 條件變數 */
}pool_t;
/*********************************************************************
*功能: 初始化執行緒池結構體並建立執行緒
*引數:
pool:執行緒池控制程式碼
threads_limit:執行緒池中執行緒的數量
*返回值: 無
*********************************************************************/
void pool_init(pool_t *pool, int threads_limit);
/*********************************************************************
*功能: 銷燬執行緒池,等待佇列中的任務不會再被執行,
但是正在執行的執行緒會一直,把任務執行完後再退出
*引數: 執行緒池控制程式碼
*返回值: 成功:0,失敗非0
*********************************************************************/
int pool_uninit(pool_t *pool);
/*********************************************************************
*功能: 向執行緒池中新增一個任務
*引數:
pool:執行緒池控制程式碼
process:任務處理函式
arg:任務引數
*返回值: 0
*********************************************************************/
int pool_add_task(pool_t *pool, pool_task_f process, void *arg);
#endif
thread_pool.c 的示例程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <assert.h>
#include "thread_pool.h"
static void *pool_thread_server(void *arg);
/*********************************************************************
*功能: 初始化執行緒池結構體並建立執行緒
*引數:
pool:執行緒池控制程式碼
threads_limit:執行緒池中執行緒的數量
*返回值: 無
*********************************************************************/
void pool_init(pool_t *pool, int threads_limit)
{
pool->threads_limit = threads_limit;
pool->queue_head = NULL;
pool->task_in_queue = 0;
pool->destroy_flag = 0;
/*建立存放執行緒ID的空間*/
pool->threadid = (pthread_t *)calloc(threads_limit, sizeof(pthread_t));
int i = 0;
/*初始化互斥鎖和條件變數*/
pthread_mutex_init(&(pool->queue_lock), NULL);
pthread_cond_init(&(pool->queue_ready), NULL);
/*迴圈建立threads_limit個執行緒*/
for (i = 0; i < threads_limit; i++){
pthread_create(&(pool->threadid[i]), NULL, pool_thread_server, pool);
}
return;
}
/*********************************************************************
*功能: 銷燬執行緒池,等待佇列中的任務不會再被執行,
但是正在執行的執行緒會一直,把任務執行完後再退出
*引數: 執行緒池控制程式碼
*返回值: 成功:0,失敗非0
*********************************************************************/
int pool_uninit(pool_t *pool)
{
pool_task *head = NULL;
int i;
pthread_mutex_lock(&(pool->queue_lock));
if(pool->destroy_flag)/* 防止兩次呼叫 */
return -1;
pool->destroy_flag = 1;
pthread_mutex_unlock(&(pool->queue_lock));
/* 喚醒所有等待執行緒,執行緒池要銷燬了 */
pthread_cond_broadcast(&(pool->queue_ready));
/* 阻塞等待執行緒退出,否則就成殭屍了 */
for (i = 0; i < pool->threads_limit; i++)
pthread_join(pool->threadid[i], NULL);
free(pool->threadid);
/* 銷燬等待佇列 */
pthread_mutex_lock(&(pool->queue_lock));
while(pool->queue_head != NULL){
head = pool->queue_head;
pool->queue_head = pool->queue_head->next;
free(head);
}
pthread_mutex_unlock(&(pool->queue_lock));
/*條件變數和互斥量也別忘了銷燬*/
pthread_mutex_destroy(&(pool->queue_lock));
pthread_cond_destroy(&(pool->queue_ready));
return 0;
}
/*********************************************************************
*功能: 向任務佇列中新增一個任務
*引數: pool:執行緒池控制程式碼
process:任務處理函式
arg:任務引數
*返回值: 無
*********************************************************************/
static void enqueue_task(pool_t *pool, pool_task_f process, void *arg)
{
pool_task *task = NULL;
pool_task *member = NULL;
pthread_mutex_lock(&(pool->queue_lock));
if(pool->task_in_queue >= pool->threads_limit){
printf("task_in_queue > threads_limit!\n");
pthread_mutex_unlock (&(pool->queue_lock));
return;
}
task = (pool_task *)calloc(1, sizeof(pool_task));
assert(task != NULL);
task->process = process;
task->arg = arg;
task->next = NULL;
pool->task_in_queue++;
member = pool->queue_head;
if(member != NULL){
while(member->next != NULL) /* 將任務加入到任務鏈連的最後位置. */
member = member->next;
member->next = task;
}else{
pool->queue_head = task; /* 如果是第一個任務的話,就指向頭 */
}
printf("\ttasks %d\n", pool->task_in_queue);
/* 等待佇列中有任務了,喚醒一個等待執行緒 */
pthread_cond_signal (&(pool->queue_ready));
pthread_mutex_unlock (&(pool->queue_lock));
}
/*********************************************************************
*功能: 從任務佇列中取出一個任務
*引數: 執行緒池控制程式碼
*返回值: 任務控制程式碼
*********************************************************************/
static pool_task *dequeue_task(pool_t *pool)
{
pool_task *task = NULL;
pthread_mutex_lock(&(pool->queue_lock));
/* 判斷執行緒池是否要銷燬了 */
if(pool->destroy_flag){
pthread_mutex_unlock(&(pool->queue_lock));
printf("thread 0x%lx will be destroyed\n", pthread_self());
pthread_exit(NULL);
}
/* 如果等待佇列為0並且不銷燬執行緒池,則處於阻塞狀態 */
if(pool->task_in_queue == 0){
while((pool->task_in_queue == 0) && (!pool->destroy_flag)){
printf("thread 0x%lx is waitting\n", pthread_self());
/* 注意:pthread_cond_wait是一個原子操作,等待前會解鎖,喚醒後會加鎖 */
pthread_cond_wait(&(pool->queue_ready), &(pool->queue_lock));
}
}else{
/* 等待佇列長度減去1,並取出佇列中的第一個元素 */
pool->task_in_queue--;
task = pool->queue_head;
pool->queue_head = task->next;
printf("thread 0x%lx received a task\n", pthread_self());
}
pthread_mutex_unlock(&(pool->queue_lock));
return task;
}
/*********************************************************************
*功能: 向執行緒池中新增一個任務
*引數: pool:執行緒池控制程式碼
process:任務處理函式
arg:任務引數
*返回值: 0
*********************************************************************/
int pool_add_task(pool_t *pool, pool_task_f process, void *arg)
{
enqueue_task(pool, process, arg);
return 0;
}
/*********************************************************************
*功能: 執行緒池服務程式
*引數: 略
*返回值: 略
*********************************************************************/
static void *pool_thread_server(void *arg)
{
pool_t *pool = NULL;
pool = (pool_t *)arg;
while(1){
pool_task *task = NULL;
task = dequeue_task(pool);
/*呼叫回撥函式,執行任務*/
if(task != NULL){
printf ("thread 0x%lx is busy\n", pthread_self());
task->process(task->arg);
free(task);
task = NULL;
}
}
/*這一句應該是不可達的*/
pthread_exit(NULL);
return NULL;
}
下面是測試程式碼:
#include <stdio.h>
#include <unistd.h>
#include "thread_pool.h"
void *task_test(void *arg)
{
printf("\t\tworking on task %d\n", (int)arg);
sleep(1); /*休息一秒,延長任務的執行時間*/
return NULL;
}
void thread_pool_demo(void)
{
pool_t pool;
int i = 0;
pool_init(&pool, 2);//初始化一個執行緒池,其中建立2個執行緒
sleep(1);
for(i = 0; i < 5; i++){
sleep(1);
pool_add_task(&pool, task_test, (void *)i);//新增一個任務
}
sleep(4);
pool_uninit(&pool);//刪除執行緒池
}
int main (int argc, char *argv[])
{
thread_pool_demo();
return 0;
}
執行結果如下:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69914734/viewspace-2657963/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 物聯網學習教程—— 執行緒私有資料執行緒
- 物聯網學習教程——執行緒同步與互斥:讀寫鎖執行緒
- 執行緒池學習執行緒
- 【java學習】ThreadPoolExecutor 執行緒池Javathread執行緒
- 深入學習執行緒池原理執行緒
- 手寫執行緒池,對照學習ThreadPoolExecutor執行緒池實現原理!執行緒thread
- Java多執行緒學習(八)執行緒池與Executor 框架Java執行緒框架
- 物聯網學習教程——if語句
- 手寫一個執行緒池,帶你學習ThreadPoolExecutor執行緒池實現原理執行緒thread
- Java 併發:執行緒、執行緒池和執行器全面教程Java執行緒
- 物聯網學習教程—Linux 可執行檔案結構與程式結構Linux
- 物聯網學習教程——switch語句
- 物聯網學習教程——if語句2
- Java執行緒池二:執行緒池原理Java執行緒
- 執行緒和執行緒池執行緒
- 執行緒 執行緒池 Task執行緒
- 多執行緒【執行緒池】執行緒
- Java執行緒池ThreadPoolExecutor極簡教程Java執行緒thread
- 執行緒池相關複習執行緒
- 物聯網學習教程—字串與指標字串指標
- 物聯網學習教程—const關鍵字
- 物聯網學習教程—檔案的定位
- 物聯網學習教程—列舉型別型別
- 執行緒池執行緒
- 執行緒池以及四種常見執行緒池執行緒
- java執行緒池趣味事:這不是執行緒池Java執行緒
- java--執行緒池--建立執行緒池的幾種方式與執行緒池操作詳解Java執行緒
- 二. 執行緒管理之執行緒池執行緒
- Android多執行緒之執行緒池Android執行緒
- kuangshenshuo-多執行緒-執行緒池執行緒
- 多執行緒之手撕執行緒池執行緒
- java多執行緒9:執行緒池Java執行緒
- 物聯網學習教程—Const用法和體會
- 物聯網學習教程—const用法的體會
- 執行緒池管理(1)-為什麼需要執行緒池執行緒
- C# 使用執行緒池佇列(學習筆記)C#執行緒佇列筆記
- Android執行緒池Android執行緒
- java 執行緒池Java執行緒