9. 使用Pthreads实现线程池(二)

结构体定义

        首先定义存储任务信息的结构体,我们可以将其抽象为函数指针和参数,如下所示

typedef struct
{
  void *(*function)(void *);// 函数指针
  void *arg;// 函数参数
}THREAD_POOL_TASK;

        接下来定义线程池的结构体,包含线程池的资源和状态等属性,如下所示

typedef struct 
{
  int thread_num;// 工作线程数量
  int task_queue_max_size;// 任务队列的容量
  int task_queue_num;// 加入的任务数量
  int task_queue_front;// 将要保存任务信息的队列索引
  int task_queue_rear;// 将要取出任务信息的队列索引
  pthread_mutex_t lock;// 线程池状态变量操作的互斥锁
  pthread_cond_t task_queue_not_full;// 任务队列有空闲空间的信号
  pthread_cond_t task_queue_not_empty;// 任务队列中有待处理任务的信号
  pthread_t *pt_threads;// 工作线程数组指针
  THREAD_POOL_TASK *pt_task_queue;// 任务队列,环形缓冲区形式
  bool shutdown;// 销毁标志
}THREAD_POOL_ST;

接口定义

1)线程池初始化

        初始化接口定义如下,入参包括线程池句柄指针,工作线程数量,任务队列容量;这里为了避免泄漏具体实现,将线程池句柄的类型设为了void*型。

bool ThreadPool_Init(void **ppv_handle, int thread_num, int task_max_size)
{
  bool ret = true;
  // 判断入参合法性
  if((ppv_handle == NULL) || (thread_num <= 0) || (task_max_size <= 0))
  {
    printf("Error: input param is invalid\n");
    ret = false;
    return ret;
  }
  // 申请线程池资源并做初始化
  THREAD_POOL_ST *pt_pool = (THREAD_POOL_ST *)malloc(sizeof(THREAD_POOL_ST));
  pt_pool->shutdown = false;
  
  pt_pool->thread_num = thread_num;
  pt_pool->pt_threads = NULL;
  if(pt_pool->thread_num > 0)
  {
    pt_pool->pt_threads = (pthread_t *)malloc(sizeof(pthread_t) * pt_pool->thread_num);
    memset(pt_pool->pt_threads, 0, sizeof(pthread_t) * pt_pool->thread_num);
  }

  pt_pool->task_queue_max_size = task_max_size;
  pt_pool->pt_task_queue = (THREAD_POOL_TASK*)malloc(sizeof(THREAD_POOL_TASK) * pt_pool->task_queue_max_size);
  pt_pool->task_queue_num = 0;
  pt_pool->task_queue_front = 0;
  pt_pool->task_queue_rear = 0;

  pthread_mutex_init(&(pt_pool->lock), NULL);
  pthread_cond_init(&(pt_pool->task_queue_not_empty), NULL);
  pthread_cond_init(&(pt_pool->task_queue_not_full), NULL);

  for(int n = 0; n < pt_pool->thread_num; n++)
  {
    pthread_create(&(pt_pool->pt_threads[n]), NULL, ThreadPool_WorkThread, (void*)pt_pool);
  }

  *ppv_handle = pt_pool;
  return ret;
}

2)工作线程

        工作线程负责竞争获取任务队列中的任务并执行,如果竞争不到任务则进入休眠。

static void ThreadPool_WorkThread(void *arg)
{
  // arg为线程池句柄
  THREAD_POOL_ST *pt_pool = (THREAD_POOL_ST *)arg;
  THREAD_POOL_TASK task;

  while(1)
  {
    // 判断线程池是否准备销毁,如果不是是否有待处理的任务;考虑误唤醒以及多线程竞争
    // 的情况这里使用的是while()循环判断
    pthread_mutex_lock(&(pt_pool->lock));
    while((pt_pool->task_queue_num == 0) && (!pt_pool->shutdown))
    {
      // 等待任务队列不为空的信号
      pthread_cond_wait(&(pt_pool->task_queue_not_empty), &(pt_pool->lock));
    }
    // 如果线程池准备销毁,则释放资源并退出
    if(pt_pool->shutdown)
    {
      pthread_mutex_unlock(&(pt_pool->lock));
      pthread_exit(NULL);
    }
	// 从任务队列取任务
    task.function = pt_pool->pt_task_queue[pt_pool->task_queue_rear].function;
    task.arg = pt_pool->pt_task_queue[pt_pool->task_queue_rear].arg;
    pt_pool->task_queue_rear = (pt_pool->task_queue_rear + 1) % pt_pool->task_queue_max_size;
    pt_pool->task_queue_num--;
    // 发送任务队列有了空闲空间的信号
    pthread_cond_broadcast(&(pt_pool->task_queue_not_full));
    pthread_mutex_unlock(&(pt_pool->lock));
    // 执行任务
    (*(task.function))(task.arg);

  }

  pthread_exit(NULL);
}

3)添加任务

        将任务信息添加到线程池的任务队列中,如果任务队列无空间则会阻塞等待。

bool ThreadPool_AddTask(void *pv_handle, void *(*function)(void *arg), void *arg)
{
  bool ret = true;
  // 判断参数合法性
  if((NULL == pv_handle) || (NULL == function))
  {
    printf("Error: input param is invalid\n");
    ret = false;
    return ret;
  }

  THREAD_POOL_ST *pt_pool = (THREAD_POOL_ST *)pv_handle;
  pthread_mutex_lock(&(pt_pool->lock));
  // 确保线程池中的任务队列有空间
  while((pt_pool->task_queue_num == pt_pool->task_queue_max_size) && (!pt_pool->shutdown))
  {
    pthread_cond_wait(&(pt_pool->task_queue_not_full), &(pt_pool->lock));
  }
  // 线程池需要销毁,释放资源后退出
  if(pt_pool->shutdown)
  {
    pthread_mutex_unlock(&(pt_pool->lock));
    ret = false;
    return ret;
  }
  // 向任务队列中添加任务
  pt_pool->pt_task_queue[pt_pool->task_queue_front].function = function;
  pt_pool->pt_task_queue[pt_pool->task_queue_front].arg = arg;
  pt_pool->task_queue_front = (pt_pool->task_queue_front + 1) % pt_pool->task_queue_max_size;
  pt_pool->task_queue_num++;
  // 发送任务队列不为空的信号
  pthread_cond_signal(&(pt_pool->task_queue_not_empty));
  pthread_mutex_unlock(&(pt_pool->lock));

  return ret;
}

4)销毁线程池

        销毁线程池时会确保线程池中任务队列的所有任务都执行完毕,再释放线程池资源。

bool ThreadPool_Destroy(void* pv_handle)
{
  // 判断入参合法性
  bool ret = true;
  if(NULL == pv_handle)
  {
    printf("Error: input param is invalid\n");
    ret = false;
    return ret;
  }

  THREAD_POOL_ST *pt_pool = (THREAD_POOL_ST *)pv_handle;
  // 确保所有任务执行完毕
  pthread_mutex_lock(&(pt_pool->lock));
  while(pt_pool->task_queue_num > 0)
  {
    pthread_mutex_unlock(&(pt_pool->lock));
#ifdef _WIN32
    Sleep(10);
#else
    usleep(10000);
#endif
    pthread_mutex_lock(&(pt_pool->lock));
  }
  // 设置线程池销毁标志,并唤醒所有工作线程使其退出
  pt_pool->shutdown = true;
  pthread_mutex_unlock(&(pt_pool->lock));
  for(int n = 0; n < pt_pool->thread_num; n++)
  {
    pthread_cond_broadcast(&(pt_pool->task_queue_not_empty));
  }

  for(int n = 0; n < pt_pool->thread_num; n++)
  {
    pthread_join(pt_pool->pt_threads[n], NULL);
  }
  // 释放线程池资源
  if(pt_pool->pt_task_queue)
  {
    free(pt_pool->pt_task_queue);
    pt_pool->pt_task_queue = NULL;
  }

  if(pt_pool->pt_threads)
  {
    free(pt_pool->pt_threads);
    pt_pool->pt_threads = NULL;
  }

  pthread_mutex_destroy(&(pt_pool->lock));
  pthread_cond_destroy(&(pt_pool->task_queue_not_empty));
  pthread_cond_destroy(&(pt_pool->task_queue_not_full));
  free(pt_pool);

  return ret;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mega_Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值