先理清思路:
線程池需要維護一個任務隊列,允許配置活動的線程數,線程池從任務隊列中取任務,根據拿到的任務執行響應處理,所以每個任務是這樣一個結構體:
typedef void* (*FUNC_POINT)(void* arg); //定義一個函數指針
typedef struct Task //任務結構體,
{
//void* (*Handler)(void* arg); //處理任務的函數
FUNC_POINT Handler;
void* url; //參數
struct Task* next; //先一個Task的指針
}Task;
線程池Pool也是一個結構體,該結構體如下:
typedef struct Pool
{
Task* head; //任務隊列頭指針,,指向任務隊列的第一個任務
Task* tail; //任務隊列尾指針,,指向任務隊列的最後一個任務
int maxThreads; //線程池的最大線程個數
int workThreads; //線程池當前的線程數
int freeThreads; //線程池空閒的線程數(指的是:已經創建,並且已經把自己任務完成了的線程)
pthread_mutex_t mut;
pthread_cond_t cond;
int destroy; //線程池銷燬標誌(值爲1銷燬)
}Pool;
所以邏輯是這樣的:
我們創建一個線程池,然後往線程池裏加任務。然後創建線程,線程從線程池結構體裏取任務(同時只能由一個線程取任務),取到任務之後調用task結構體中的函數就可。
threadpool_add(Pool* pool, FUNC_POINT fun, void *url);//添加任務到線程池
第一個參數是線程池結構體指針,
第二個參數任務處理函數,就是線程拿到任務之後執行什麼任務;
第三個參數是任務處理函數的參數的地址(因爲我們要傳什麼參數需要視情況而定,可能是int,char*或者其他,所以定義一個void*指針,把參數地址傳過去就行了)
具體代碼如下:
threadpool.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <wait.h>
#include <time.h>
#include <errno.h>
#include <sys/time.h>
typedef void* (*FUNC_POINT)(void* arg); //定義一個函數指針
typedef struct Task //任務結構體,
{
//void* (*Handler)(void* arg); //處理任務的函數
FUNC_POINT Handler;
void* url; //參數
struct Task* next; //先一個Task的指針
}Task;
typedef struct Pool
{
Task* head; //任務隊列頭指針,,指向任務隊列的第一個任務
Task* tail; //任務隊列尾指針,,指向任務隊列的最後一個任務
int maxThreads; //線程池的最大線程個數
int workThreads; //線程池當前的線程數
int freeThreads; //線程池空閒的線程數(指的是:已經創建,並且已經把自己任務完成了的線程)
pthread_mutex_t mut;
pthread_cond_t cond;
int destroy; //線程池銷燬標誌(值爲1銷燬)
}Pool;
void* thread_Handler(void* arg); //線程處理函數
void thread_init(Pool* pool, int max); //線程初始化
void* thread_Handler(void* arg)
{
int timeout;
timeout = 0;
Pool* pool = (Pool*)arg;
while(1)
{
//上鎖
pthread_mutex_lock(&pool->mut);
//增加當前的空閒線程數
pool->freeThreads++;
//如果現在沒有任務,並且也沒有受到線程池銷燬的通知
while(pool->head == NULL && pool->destroy == 0)
{
printf("任務隊列空!thread:0x%0x is waiting\n",pthread_self());
// int err;
// err = pthread_cond_wait(&pool->cond, &pool->mut);
// if(err != 0){
// printf("pthread_cond_wait is fail\n");
// break;
// }
// break;
//此處使用pthread_cond_timewait。指定等待指定的時間
//必須要等,雖然此時任務隊列爲空,但可能有的任務還沒到任務隊列,還在路上
struct timespec tsp;
struct timeval now;
gettimeofday(&now, NULL);//獲取當前時間
tsp.tv_sec = now.tv_sec;
tsp.tv_nsec = now.tv_usec * 1000;
tsp.tv_sec += 10; //指定等待時間爲10s
int status;
status = pthread_cond_timedwait(&pool->cond, &pool->mut, &tsp);
if(status == ETIMEDOUT){
timeout = 1;
printf("%d freeThreads\n", pool->freeThreads);
break;
}
}
//如果有任務
if(pool->head != NULL){
pool->freeThreads--;
printf("拿到一個任務\n");
Task* tmp = pool->head;
pool->head = tmp->next;
//開始執行任務函數
//此處要解鎖,執行任務需要一定時間,這段時間內要允許添加任務
//其他消費者線程能夠進入等待任務
pthread_mutex_unlock(&pool->mut);
tmp->Handler(tmp->url);
free(tmp);
pthread_mutex_lock(&pool->mut);
}
//如果現在沒有任務,但是收到線程池銷燬的通知
if(pool->head == NULL && pool->destroy == 1){
printf("銷燬通知\n");
printf("%d workThreads\n",pool->workThreads);
pool->workThreads--;
if(pool->workThreads == 0){
pthread_cond_signal(&pool->cond);
}
pthread_mutex_unlock(&pool->mut);
break; //退出循環
}
//等待超時
if(timeout == 1 && pool->head == NULL){
printf("thread:0x%0x waiting timeout\n",pthread_self());
pool->workThreads--;
pthread_cond_signal(&pool->cond);
pthread_mutex_unlock(&pool->mut);
break;
}
//解鎖
pthread_mutex_unlock(&pool->mut);
}
printf("thread: 0x%0x is exiting!\n", (int)pthread_self());
return NULL;
}
void thread_init(Pool* pool, int max)
{
//初始化條件變量和互斥
if(pthread_mutex_init(&pool->mut, NULL) != 0)
{
perror("mutex_init");
return;
}
if(pthread_cond_init(&pool->cond, NULL) != 0)
{
perror("cond_init");
return;
}
//初始化線程池
pool->workThreads = 0;
pool->freeThreads = 0;
pool->maxThreads = max;
pool->head = NULL;
pool->tail = NULL;
pool->destroy = 0;
}
void thread_add(Pool* pool, FUNC_POINT func, void* url)
{
//生成一個新任務
printf("新任務!\n");
Task* newTask = (Task*)malloc(sizeof(Task));
newTask->Handler = func;
newTask->url = url;
newTask->next = NULL;
//把新任務添加到任務隊列
pthread_mutex_lock(&pool->mut);
//如果當前任務隊列是空的
if(pool->head == NULL)
pool->head = newTask;
else //否則,新的任務放在隊列最後
pool->tail->next = newTask;
pool->tail = newTask; //更新尾指針
//喚醒空閒線程
if(pool->freeThreads > 0){
// 喚醒
pthread_cond_signal(&pool->cond);
}else if((pool->workThreads) < (pool->maxThreads)){
// 創建新線程
printf("創建線程\n");
pthread_t id;
pthread_create(&id, NULL, thread_Handler, pool);
pool->workThreads++;
}
pthread_mutex_unlock(&pool->mut);
}
void thread_destroy(Pool* pool)
{
if(pool->destroy == 1)//如果該線程池已經銷燬過了,直接退出
return;
//銷燬線程池
printf("%d workThreads\n",pool->workThreads);
pthread_mutex_lock(&pool->mut);
if(pool->maxThreads > 0){
if(pool->freeThreads > 0){
pthread_cond_broadcast(&pool->cond);//喚醒所有線程,檢查是否還有工作需要完成
}
while(pool->workThreads > 0){
pthread_cond_wait(&pool->cond, &pool->mut); //如果有在工作的線程,需要等待
}
}
pool->destroy = 1;//到此處說明所有線程已經完成任務,workThreads=0。退出標誌設爲一
int err;
err = pthread_mutex_unlock(&pool->mut);
printf("err=%d\n",err);
err = pthread_mutex_destroy(&pool->mut);
printf("err=%d,互斥量銷燬\n",err);
err = pthread_cond_destroy(&pool->cond);
printf("err=%d,條件變量銷燬\n",err);
printf("線程池已銷燬\n");
}
threadpool.c
#include "pthreadpool.h"
/*
*我的實現思路:
*線程池結構體是Pool,配置活動線程數是通過main函數參數傳入的
*線程池內維護了一個任務隊列,,每個任務是一個結構體,成員是任務處理函數的地址,參數,和指向下一個任務的指針。
*活動的線程依次從任務隊列取任務,
*每個線程取到任務之後執行任務處理函數,該函數fork()一個子進程,由子進程執行程序替換,
*完成工作的線程在任務隊列爲空的情況下會等到10秒,減少工作線程數量,解鎖,然後線程就退出,
*/
/*
*解決線程池的銷燬工作:(這一次解決了前一次存在的問題:1,線程沒有完成任務情況下,main函數退出
2,在線程池銷燬函數中陷入死循環)
*
* 解決方法:(這裏在參考了CSDN()[原出處](https://blog.csdn.net/m0_38126105/article/details/79251832)上一個人實現線程池時,對線程池銷燬的做法)
* 線程銷燬函數在main函數中主動調用,在銷燬函數內等待所有線程完成任務都成爲空閒線程,並且
* workthreads==0時纔會退出。解決了main函數提前退出的問題。
*/
char URL[][100] = {
{"https://dl.softmgr.qq.com/original/Browser/QQBrowser_Setup_Qqpcmgr_10.4.3587.400.exe"},
{"https://dl.softmgr.qq.com/original/Browser/Firefox_Setup_68.0.1_bzb32.exe"},
{"https://dl.softmgr.qq.com/original/Development/npp.7.7.1.Installer.exe"},
{"https://sm.myapp.com/original/Development/epp500_0651_64bit-5.0.651.0.exe"},
{"https://sm.myapp.com/original/Download/fhsetup_8843-3.0.2.exe"},
{"https://dl.softmgr.qq.com/original/Compression/BANDIZIP-SETUP_6.24.0.1.EXE"},
{"https://dl.softmgr.qq.com/original/Compression/7z1900-x64.exe"},
{"https://sm.myapp.com/original/Compression/SpeedZipSetupV2.1.6.6.exe"},
{"https://sm.myapp.com/original/Compression/WinZip-cn_20.0.12033.exe"},
{"https://dl.softmgr.qq.com/original/Compression/winrar-x64-571scp.exe"}
};
void* fun(void* arg)//任務處理函數
{
pid_t id;
if((id = fork())<0){
perror("fork");
return NULL;
}else if(id == 0){
execlp("wget", "wget", "-q", arg, NULL);
}else{
wait(NULL);
}
free(arg);
return NULL;
}
//*******************************************************//
//**********************main函數*************************//
//*******************************************************//
int main(int argc, char* argv[])
{
if(argc != 2){
printf("Error:[./pthreadpool] [threadnums]\n");
return 1;
}
int threadNums;
threadNums = atoi(argv[1]);
Pool pool;
thread_init(&pool, threadNums);
int i;
for(i=0; i<10; ++i)
{
char* ret = (char*)malloc(sizeof(char)*100);
memcpy(ret, URL[i], 100);
thread_add(&pool, fun, ret);
}
signal(SIGCHLD, NULL);
thread_destroy(&pool);
printf("main is over!\n");
return 0;
}
相關問題處理分析: