c++簡單線程池實現

線程池,簡單來說就是有一堆已經創建好的線程(最大數目一定),初始時他們都處于空閑狀態,當有新的任務進來,從線程池中取出一個空閑的線程處理任務,然后當任務處理完成之后,該線程被重新放回到線程池中,供其他的任務使用,當線程池中的線程都在處理任務時,就沒有空閑線程供使用,此時,若有新的任務產生,只能等待線程池中有線程結束任務空閑才能執行,下面是線程池的工作原理圖:

我們為什么要使用線程池呢?

簡單來說就是線程本身存在開銷,我們利用多線程來進行任務處理,單線程也不能濫用,無止禁的開新線程會給系統產生大量消耗,而線程本來就是可重用的資源,不需要每次使用時都進行初始化,因此可以采用有限的線程個數處理無限的任務。

?

廢話少說,直接上代碼

首先是用條件變量和互斥量封裝的一個狀態,用于保護線程池的狀態

condition.h

復制代碼
#ifndef _CONDITION_H_
#define _CONDITION_H_#include <pthread.h>//封裝一個互斥量和條件變量作為狀態
typedef struct condition
{pthread_mutex_t pmutex;pthread_cond_t pcond;
}condition_t;//對狀態的操作函數
int condition_init(condition_t *cond);
int condition_lock(condition_t *cond);
int condition_unlock(condition_t *cond);
int condition_wait(condition_t *cond);
int condition_timedwait(condition_t *cond, const struct timespec *abstime);
int condition_signal(condition_t* cond);
int condition_broadcast(condition_t *cond);
int condition_destroy(condition_t *cond);#endif
復制代碼

condition.c

復制代碼
#include "condition.h"//初始化
int condition_init(condition_t *cond)
{int status;if((status = pthread_mutex_init(&cond->pmutex, NULL)))return status;if((status = pthread_cond_init(&cond->pcond, NULL)))return status;return 0;
}//加鎖
int condition_lock(condition_t *cond)
{return pthread_mutex_lock(&cond->pmutex);
}//解鎖
int condition_unlock(condition_t *cond)
{return pthread_mutex_unlock(&cond->pmutex);
}//等待
int condition_wait(condition_t *cond)
{return pthread_cond_wait(&cond->pcond, &cond->pmutex);
}//固定時間等待
int condition_timedwait(condition_t *cond, const struct timespec *abstime)
{return pthread_cond_timedwait(&cond->pcond, &cond->pmutex, abstime);
}//喚醒一個睡眠線程
int condition_signal(condition_t* cond)
{return pthread_cond_signal(&cond->pcond);
}//喚醒所有睡眠線程
int condition_broadcast(condition_t *cond)
{return pthread_cond_broadcast(&cond->pcond);
}//釋放
int condition_destroy(condition_t *cond)
{int status;if((status = pthread_mutex_destroy(&cond->pmutex)))return status;if((status = pthread_cond_destroy(&cond->pcond)))return status;return 0;
}
復制代碼

然后是線程池對應的threadpool.h和threadpool.c

復制代碼
#ifndef _THREAD_POOL_H_
#define _THREAD_POOL_H_//線程池頭文件

#include "condition.h"//封裝線程池中的對象需要執行的任務對象
typedef struct task
{void *(*run)(void *args);  //函數指針,需要執行的任務void *arg;              //參數struct task *next;      //任務隊列中下一個任務
}task_t;//下面是線程池結構體
typedef struct threadpool
{condition_t ready;    //狀態量task_t *first;       //任務隊列中第一個任務task_t *last;        //任務隊列中最后一個任務int counter;         //線程池中已有線程數int idle;            //線程池中kongxi線程數int max_threads;     //線程池最大線程數int quit;            //是否退出標志
}threadpool_t;//線程池初始化
void threadpool_init(threadpool_t *pool, int threads);//往線程池中加入任務
void threadpool_add_task(threadpool_t *pool, void *(*run)(void *arg), void *arg);//摧毀線程池
void threadpool_destroy(threadpool_t *pool);#endif
復制代碼
復制代碼
#include "threadpool.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>//創建的線程執行
void *thread_routine(void *arg)
{struct timespec abstime;int timeout;printf("thread %d is starting\n", (int)pthread_self());threadpool_t *pool = (threadpool_t *)arg;while(1){timeout = 0;//訪問線程池之前需要加鎖condition_lock(&pool->ready);//空閑pool->idle++;//等待隊列有任務到來 或者 收到線程池銷毀通知while(pool->first == NULL && !pool->quit){//否則線程阻塞等待printf("thread %d is waiting\n", (int)pthread_self());//獲取從當前時間,并加上等待時間, 設置進程的超時睡眠時間clock_gettime(CLOCK_REALTIME, &abstime);  abstime.tv_sec += 2;int status;status = condition_timedwait(&pool->ready, &abstime);  //該函數會解鎖,允許其他線程訪問,當被喚醒時,加鎖if(status == ETIMEDOUT){printf("thread %d wait timed out\n", (int)pthread_self());timeout = 1;break;}}pool->idle--;if(pool->first != NULL){//取出等待隊列最前的任務,移除任務,并執行任務task_t *t = pool->first;pool->first = t->next;//由于任務執行需要消耗時間,先解鎖讓其他線程訪問線程池condition_unlock(&pool->ready);//執行任務t->run(t->arg);//執行完任務釋放內存free(t);//重新加鎖condition_lock(&pool->ready);}//退出線程池if(pool->quit && pool->first == NULL){pool->counter--;//當前工作的線程數-1//若線程池中沒有線程,通知等待線程(主線程)全部任務已經完成if(pool->counter == 0){condition_signal(&pool->ready);}condition_unlock(&pool->ready);break;}//超時,跳出銷毀線程if(timeout == 1){pool->counter--;//當前工作的線程數-1condition_unlock(&pool->ready);break;}condition_unlock(&pool->ready);}printf("thread %d is exiting\n", (int)pthread_self());return NULL;}//線程池初始化
void threadpool_init(threadpool_t *pool, int threads)
{condition_init(&pool->ready);pool->first = NULL;pool->last =NULL;pool->counter =0;pool->idle =0;pool->max_threads = threads;pool->quit =0;}//增加一個任務到線程池
void threadpool_add_task(threadpool_t *pool, void *(*run)(void *arg), void *arg)
{//產生一個新的任務task_t *newtask = (task_t *)malloc(sizeof(task_t));newtask->run = run;newtask->arg = arg;newtask->next=NULL;//新加的任務放在隊列尾端//線程池的狀態被多個線程共享,操作前需要加鎖condition_lock(&pool->ready);if(pool->first == NULL)//第一個任務加入
    {pool->first = newtask;}        else    {pool->last->next = newtask;}pool->last = newtask;  //隊列尾指向新加入的線程//線程池中有線程空閑,喚醒if(pool->idle > 0){condition_signal(&pool->ready);}//當前線程池中線程個數沒有達到設定的最大值,創建一個新的線性else if(pool->counter < pool->max_threads){pthread_t tid;pthread_create(&tid, NULL, thread_routine, pool);pool->counter++;}//結束,訪問condition_unlock(&pool->ready);
}//線程池銷毀
void threadpool_destroy(threadpool_t *pool)
{//如果已經調用銷毀,直接返回if(pool->quit){return;}//加鎖condition_lock(&pool->ready);//設置銷毀標記為1pool->quit = 1;//線程池中線程個數大于0if(pool->counter > 0){//對于等待的線程,發送信號喚醒if(pool->idle > 0){condition_broadcast(&pool->ready);}//正在執行任務的線程,等待他們結束任務while(pool->counter){condition_wait(&pool->ready);}}condition_unlock(&pool->ready);condition_destroy(&pool->ready);
}
復制代碼

測試代碼:

復制代碼
#include "threadpool.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>void* mytask(void *arg)
{printf("thread %d is working on task %d\n", (int)pthread_self(), *(int*)arg);sleep(1);free(arg);return NULL;
}//測試代碼
int main(void)
{threadpool_t pool;//初始化線程池,最多三個線程threadpool_init(&pool, 3);int i;//創建十個任務for(i=0; i < 10; i++){int *arg = malloc(sizeof(int));*arg = i;threadpool_add_task(&pool, mytask, arg);}threadpool_destroy(&pool);return 0;
}
復制代碼

輸出結果:

可以看出程序先后創建了三個線程進行工作,當沒有任務空閑時,等待2s直接退出銷毀線程

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/385003.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/385003.shtml
英文地址,請注明出處:http://en.pswp.cn/news/385003.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux 打印可變參數日志

實現了傳輸進去的字符串所在的文檔&#xff0c;函數和行數顯示功能。 實現了將傳入的可變參數打印到日志功能。 #include<stdio.h> #include<stdarg.h> #include<string.h>const char * g_path "/home/exbot/wangqinghe/log.txt"; #define LOG(fm…

C++強化之路之線程池開發整體框架(二)

一.線程池開發框架 我所開發的線程池由以下幾部分組成&#xff1a; 1.工作中的線程。也就是線程池中的線程&#xff0c;主要是執行分發來的task。 2.管理線程池的監督線程。這個線程的創建獨立于線程池的創建&#xff0c;按照既定的管理方法進行管理線程池中的所有線程&#xf…

vfprintf()函數

函數聲明&#xff1a;int vfprintf(FILE *stream, const char *format, va_list arg) 函數參數&#xff1a; stream—這是指向了FILE對象的指針&#xff0c;該FILE對象標識了流。 format—c語言字符串&#xff0c;包含了要被寫入到流stream中的文本。它可以包含嵌入的format標簽…

Makefile(二)

將生產的.o文件放進指定的文件中&#xff08;先創建該文件夾&#xff09; src $(wildcard ./*.cpp) obj $(patsubst %.cpp,./output/%.o,$(src))target test$(target) : $(obj)g $(obj) -o $(target) %.o: %.cppg -c $< -o output/$.PHONY:clean clean:rm -f $(target) $…

TCP粘包問題分析和解決(全)

TCP通信粘包問題分析和解決&#xff08;全&#xff09;在socket網絡程序中&#xff0c;TCP和UDP分別是面向連接和非面向連接的。因此TCP的socket編程&#xff0c;收發兩端&#xff08;客戶端和服務器端&#xff09;都要有成對的socket&#xff0c;因此&#xff0c;發送端為了將…

UML類圖符號 各種關系說明以及舉例

UML中描述對象和類之間相互關系的方式包括&#xff1a;依賴&#xff0c;關聯&#xff0c;聚合&#xff0c;組合&#xff0c;泛化&#xff0c;實現等。表示關系的強弱&#xff1a;組合>聚合>關聯>依賴 相互間關系 聚合是表明對象之間的整體與部分關系的關聯&#xff0c…

尋找數組中第二大數

設置兩個數值來表示最大數和第二大數&#xff0c;在循環比較賦值即可 //找給定數組中第二大的數int get_smax(int *arr,int length) {int max;int smax;if(arr[0] > arr[1]){max arr[0];smax arr[1];}else{max arr[1];smax arr[0];}for(int i 2; i < length; i){if(…

timerfd API使用總結

timerfd 介紹 timerfd 是在Linux內核2.6.25版本中添加的接口&#xff0c;其是Linux為用戶提供的一個定時器接口。這個接口基于文件描述符&#xff0c;所以可以被用于select/poll/epoll的場景。當使用timerfd API創建多個定時器任務并置于poll中進行事件監聽&#xff0c;當沒有可…

#if/#else/#endif

在linux環境下寫c代碼時會嘗試各種方法或調整路徑&#xff0c;需要用到#if #include<stdio.h>int main(){int i; #if 0i 1; #elsei 2; #endifprintf("i %d",i);return 0; } 有時候會調整代碼&#xff0c;但是又不是最終版本的更換某些值&#xff0c;就需要注…

內存分配調用

通過函數給實參分配內存&#xff0c;可以通過二級指針實現 #include<stdio.h> #incldue<stdlib.h>void getheap(int *p) //錯誤的模型 {p malloc(100); }void getheap(int **p) //正確的模型 {*p malloc(100); } int main() {int *p NULL;getheap(&p);free(p…

ESP傳輸模式拆解包流程

一、 ESP簡介ESP&#xff0c;封裝安全載荷協議(Encapsulating SecurityPayloads)&#xff0c;是一種Ipsec協議&#xff0c;用于對IP協議在傳輸過程中進行數據完整性度量、來源認證、加密以及防回放攻擊。可以單獨使用&#xff0c;也可以和AH一起使用。在ESP頭部之前的IPV4…

結構體成員內存對齊

#include<stdio.h> struct A {int A; };int main() {struct A a;printf("%d\n",sizeof(a));return 0; } 運行結果&#xff1a;4 #include<stdio.h> struct A {int a;int b&#xff1b; };int main() {struct A a;printf("%d\n",sizeof(a))…

C庫函數-fgets()

函數聲明&#xff1a;char *fgets(char *str,int n,FILE *stream) 函數介紹&#xff1a;從指定的stream流中讀取一行&#xff0c;并把它存儲在str所指向的字符串中。當讀取到&#xff08;n-1&#xff09;個字符時&#xff0c;獲取讀取到換行符時&#xff0c;或者到達文件末尾時…

linux內核netfilter模塊分析之:HOOKs點的注冊及調用

1: 為什么要寫這個東西?最近在找工作,之前netfilter 這一塊的代碼也認真地研究過&#xff0c;應該每個人都是這樣的你懂 不一定你能很準確的表達出來。 故一定要化些時間把這相關的東西總結一下。 0&#xff1a;相關文檔linux 下 nf_conntrack_tuple 跟蹤記錄 其中可以根據內…

指定結構體元素的位字段

struct B {char a:4; //a這個成員值占了4bitchar b:2;char c:2; } 占了1個字節 struct B {int a:4; //a這個成員值占了4bitchar b:2;char c:2; } 占了8個字節 控制LED燈的結構體&#xff1a; struct E {char a1:1;char a2:1;char a3:1;char a4:1;char a5:1;char a6:1;char a7:1…

網絡抓包工具 wireshark 入門教程

Wireshark&#xff08;前稱Ethereal&#xff09;是一個網絡數據包分析軟件。網絡數據包分析軟件的功能是截取網絡數據包&#xff0c;并盡可能顯示出最為詳細的網絡數據包數據。Wireshark使用WinPCAP作為接口&#xff0c;直接與網卡進行數據報文交換。網絡管理員使用Wireshark來…

結構體中指針

結構體中帶有指針的情況 #include<stdio.h>struct man {char *name;int age; };int main() {struct man m {"tom",20};printf("name %s, age %d\n",m.name,m.age);return 0; } 運行結果&#xff1a; exbotubuntu:~/wangqinghe/C/20190714$ gcc st…

python使用opencv提取視頻中的每一幀、最后一幀,并存儲成圖片

提取視頻每一幀存儲圖片 最近在搞視頻檢測問題&#xff0c;在用到將視頻分幀保存為圖片時&#xff0c;圖片可以保存&#xff0c;但是會出現(-215:Assertion failed) !_img.empty() in function cv::imwrite問題而不能正常運行&#xff0c;在檢查代碼、檢查路徑等措施均無果后&…

結構體參數

結構體作為函數參數&#xff1a; #include<stdio.h> #include<stdlib.h> #include<string.h>struct student {char name[10];int age; };void print_student(struct student s) {printf("name %s,age %d\n",s.name,s.age); } void set_studen…

線程間通信之eventfd

線程間通信之eventfd man手冊中的解釋&#xff1a; eventfd()創建了一個“eventfd對象”&#xff0c; 通過它能夠實現用戶態程序間(我覺得這里主要指線程而非進程)的等待/通知機制&#xff0c;以及內核態向用戶態通知的機制&#xff08;未考證&#xff09;。 此對象包含了一個…