Linux企業級專案實踐之網路爬蟲(26)——執行緒池
一旦有一個抓取請求開始,就建立一個新的執行緒,由該執行緒執行任務,任務執行完畢之後,執行緒就退出。這就是"即時建立,即時銷燬"的策略。儘管與建立程式相比,建立執行緒的時間已經大大的縮短,但是如果提交給執行緒的任務是執行時間較短,而且執行次數非常頻繁,那麼伺服器就將處於一個不停的建立執行緒和銷燬執行緒的狀態。這筆開銷是不可忽略的,尤其是執行緒執行的時間非常非常短的情況。
執行緒池就是為了解決上述問題的,它的實現原理是這樣的:在應用程式啟動之後,就馬上建立一定數量的執行緒,放入空閒的佇列中。這些執行緒都是處於阻塞狀態,這些執行緒只佔一點記憶體,不佔用CPU。當任務到來後,執行緒池將選擇一個空閒的執行緒,將任務傳入此執行緒中執行。當所有的執行緒都處在處理任務的時候,執行緒池將自動建立一定的數量的新執行緒,用於處理更多的任務。執行任務完成之後執行緒並不退出,而是繼續線上程池中等待下一次任務。當大部分執行緒處於阻塞狀態時,執行緒池將自動銷燬一部分的執行緒,回收系統資源。
下面是通過一個執行緒池的實現,來解決多工抓取問題。
處理流程如下:
程式啟動之前,初始化執行緒池,啟動執行緒池中的執行緒,由於還沒有任務到來,執行緒池中的所有執行緒都處在阻塞狀態,當一有任務到達就從執行緒池中取出一個空閒執行緒處理,如果所有的執行緒都處於工作狀態,就新增到佇列,進行排隊。如果佇列中的任務個數大於佇列的所能容納的最大數量,那就不能新增任務到佇列中,只能等待佇列不滿才能新增任務到佇列中。
struct job
{
void* (*callback_function)(void *arg); //執行緒回撥函式
void *arg; //回撥函式引數
struct job *next;
};
struct threadpool
{
int thread_num; //執行緒池中開啟執行緒的個數
int queue_max_num; //佇列中最大job的個數
struct job *head; //指向job的頭指標
struct job *tail; //指向job的尾指標
pthread_t *pthreads; //執行緒池中所有執行緒的pthread_t
pthread_mutex_t mutex; //互斥訊號量
pthread_cond_t queue_empty; //佇列為空的條件變數
pthread_cond_t queue_not_empty; //佇列不為空的條件變數
pthread_cond_t queue_not_full; //佇列不為滿的條件變數
int queue_cur_num; //佇列當前的job個數
int queue_close; //佇列是否已經關閉
int pool_close; //執行緒池是否已經關閉
};
struct threadpool* threadpool_init(intthread_num, int queue_max_num)
{
struct threadpool *pool = NULL;
do
{
pool = malloc(sizeof(struct threadpool));
if (NULL == pool)
{
printf("failed to malloc threadpool!\n");
break;
}
pool->thread_num = thread_num;
pool->queue_max_num = queue_max_num;
pool->queue_cur_num = 0;
pool->head = NULL;
pool->tail = NULL;
if (pthread_mutex_init(&(pool->mutex), NULL))
{
printf("failed to init mutex!\n");
break;
}
if (pthread_cond_init(&(pool->queue_empty), NULL))
{
printf("failed to init queue_empty!\n");
break;
}
if (pthread_cond_init(&(pool->queue_not_empty), NULL))
{
printf("failed to init queue_not_empty!\n");
break;
}
if (pthread_cond_init(&(pool->queue_not_full), NULL))
{
printf("failed to init queue_not_full!\n");
break;
}
pool->pthreads = malloc(sizeof(pthread_t) * thread_num);
if (NULL == pool->pthreads)
{
printf("failed to malloc pthreads!\n");
break;
}
pool->queue_close = 0;
pool->pool_close = 0;
int i;
for (i = 0; i < pool->thread_num; ++i)
{
pthread_create(&(pool->pthreads[i]), NULL, threadpool_function,(void *)pool);
}
return pool;
}while (0);
return NULL;
}
int threadpool_add_job(struct threadpool*pool, void* (*callback_function)(void *arg), void *arg)
{
assert(pool != NULL);
assert(callback_function != NULL);
assert(arg != NULL);
pthread_mutex_lock(&(pool->mutex));
while ((pool->queue_cur_num == pool->queue_max_num) &&!(pool->queue_close || pool->pool_close))
{
pthread_cond_wait(&(pool->queue_not_full),&(pool->mutex)); //佇列滿的時候就等待
}
if (pool->queue_close || pool->pool_close) //佇列關閉或者執行緒池關閉就退出
{
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
struct job *pjob =(struct job*) malloc(sizeof(struct job));
if (NULL == pjob)
{
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
pjob->callback_function = callback_function;
pjob->arg = arg;
pjob->next = NULL;
if (pool->head == NULL)
{
pool->head = pool->tail = pjob;
pthread_cond_broadcast(&(pool->queue_not_empty)); //佇列空的時候,有任務來時就通知執行緒池中的執行緒:佇列非空
}
else
{
pool->tail->next = pjob;
pool->tail = pjob;
}
pool->queue_cur_num++;
pthread_mutex_unlock(&(pool->mutex));
return 0;
}
void* threadpool_function(void* arg)
{
struct threadpool *pool = (struct threadpool*)arg;
struct job *pjob = NULL;
while (1) //死迴圈
{
pthread_mutex_lock(&(pool->mutex));
while ((pool->queue_cur_num == 0) &&!pool->pool_close) //佇列為空時,就等待佇列非空
{
pthread_cond_wait(&(pool->queue_not_empty),&(pool->mutex));
}
if (pool->pool_close) //執行緒池關閉,執行緒就退出
{
pthread_mutex_unlock(&(pool->mutex));
pthread_exit(NULL);
}
pool->queue_cur_num--;
pjob = pool->head;
if (pool->queue_cur_num == 0)
{
pool->head = pool->tail = NULL;
}
else
{
pool->head = pjob->next;
}
if (pool->queue_cur_num == 0)
{
pthread_cond_signal(&(pool->queue_empty)); //佇列為空,就可以通知threadpool_destroy函式,銷燬執行緒函式
}
if (pool->queue_cur_num == pool->queue_max_num - 1)
{
pthread_cond_broadcast(&(pool->queue_not_full)); //佇列非滿,就可以通知threadpool_add_job函式,新增新任務
}
pthread_mutex_unlock(&(pool->mutex));
(*(pjob->callback_function))(pjob->arg); //執行緒真正要做的工作,回撥函式的呼叫
free(pjob);
pjob = NULL;
}
}
int threadpool_destroy(struct threadpool*pool)
{
assert(pool != NULL);
pthread_mutex_lock(&(pool->mutex));
if (pool->queue_close || pool->pool_close) //執行緒池已經退出了,就直接返回
{
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
pool->queue_close = 1; //置佇列關閉標誌
while (pool->queue_cur_num != 0)
{
pthread_cond_wait(&(pool->queue_empty), &(pool->mutex)); //等待佇列為空
}
pool->pool_close = 1; //置執行緒池關閉標誌
pthread_mutex_unlock(&(pool->mutex));
pthread_cond_broadcast(&(pool->queue_not_empty)); //喚醒執行緒池中正在阻塞的執行緒
pthread_cond_broadcast(&(pool->queue_not_full)); //喚醒新增任務的threadpool_add_job函式
int i;
for (i = 0; i < pool->thread_num; ++i)
{
pthread_join(pool->pthreads[i], NULL); //等待執行緒池的所有執行緒執行完畢
}
pthread_mutex_destroy(&(pool->mutex)); //清理資源
pthread_cond_destroy(&(pool->queue_empty));
pthread_cond_destroy(&(pool->queue_not_empty));
pthread_cond_destroy(&(pool->queue_not_full));
free(pool->pthreads);
struct job *p;
while (pool->head != NULL)
{
p = pool->head;
pool->head = p->next;
free(p);
}
free(pool);
return 0;
}
相關文章
- Linux企業級專案實踐之網路爬蟲(28)——爬蟲socket處理Linux爬蟲
- Linux企業級專案實踐之網路爬蟲(7)——DNS解析Linux爬蟲DNS
- Linux企業級專案實踐之網路爬蟲(19)——epoll介面Linux爬蟲
- Linux企業級專案實踐之網路爬蟲(4)——主程式流程Linux爬蟲
- Linux企業級專案實踐之網路爬蟲(8)——認識URLLinux爬蟲
- Linux企業級專案實踐之網路爬蟲(25)——管理原始碼之SVNLinux爬蟲原始碼
- Linux企業級專案實踐之網路爬蟲(5)——處理配置檔案Linux爬蟲
- Linux企業級專案實踐之網路爬蟲(21)——擴充套件為多工爬蟲Linux爬蟲套件
- Linux企業級專案實踐之網路爬蟲(17)——儲存頁面Linux爬蟲
- Linux企業級專案實踐之網路爬蟲(18)——佇列處理Linux爬蟲佇列
- Linux企業級專案實踐之網路爬蟲(27)——多路IO複用Linux爬蟲
- Linux企業級專案實踐之網路爬蟲(29)——遵守robots.txtLinux爬蟲
- Linux企業級專案實踐之網路爬蟲(13)——處理user-agentLinux爬蟲
- Linux企業級專案實踐之網路爬蟲(10)——處理HTTP狀態碼Linux爬蟲HTTP
- Linux企業級專案實踐之網路爬蟲(11)——處理http請求頭Linux爬蟲HTTP
- Linux企業級專案實踐之網路爬蟲(12)——處理HTTP應答頭Linux爬蟲HTTP
- Linux企業級專案實踐之網路爬蟲(24)——定製規則擴充套件為垂直爬蟲Linux爬蟲套件
- Linux企業級專案實踐之網路爬蟲(9)——通過URL抓取網頁內容Linux爬蟲網頁
- Linux企業級專案實踐之網路爬蟲(22)——編寫爬蟲系統服務控制指令碼Linux爬蟲指令碼
- Linux企業級專案實踐之網路爬蟲(6)——將程式設計成為守護程式Linux爬蟲程式設計
- Linux企業級專案實踐之網路爬蟲(15)——區分文字檔案和二進位制檔案Linux爬蟲
- Linux企業級專案實踐之網路爬蟲(14)——使用正規表示式抽取HTML正文和URLLinux爬蟲HTML
- Linux企業級專案實踐之網路爬蟲(20)——擴充套件成為規則外掛模式Linux爬蟲套件模式
- Linux企業級專案實踐之網路爬蟲(23)——系統測試:找出系統中的bugLinux爬蟲
- Linux企業級專案實踐之網路爬蟲(16)——使用base64傳輸二進位制資料Linux爬蟲
- Linux企業級專案實踐之網路爬蟲(30)——通過查閱RFC文件擴充更加複雜的功能Linux爬蟲
- Rocksdb專案中的工業級執行緒池執行緒
- 網路爬蟲專案爬蟲
- java執行緒池實踐Java執行緒
- Android執行緒池的原理以及專案中實踐Android執行緒
- 企業資料爬蟲專案爬蟲
- 網路爬蟲專案蒐集爬蟲
- 執行緒池原理與實踐執行緒
- 多執行緒爬蟲實現(上)執行緒爬蟲
- 執行緒(一)——執行緒,執行緒池,Task概念+程式碼實踐執行緒
- 美團動態執行緒池實踐思路開源專案(DynamicTp),執行緒池原始碼解析及通知告警篇執行緒原始碼
- 企業資料爬蟲專案(二)爬蟲
- Python網路爬蟲實戰專案大全 32個Python爬蟲專案demoPython爬蟲