基于POSIX实现一个文件下载线程池

20 篇文章 0 订阅

先理清思路:
线程池需要维护一个任务队列,允许配置活动的线程数,线程池从任务队列中取任务,根据拿到的任务执行响应处理,所以每个任务是这样一个结构体:

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;
}

相关问题处理分析:
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值