(P41)一个简单的线程池实现

1.线程池说明

  • 线程池拥有若干个线程
  • 用于执行大量相对短暂的任务
    由于线程个数小于任务的数量,因而任务需要进入队列中进行等待,在短时间可以得到一个线程对任务进行处理
  • 线程的个数与任务的类型有关
    (1)计算密集型任务: 执行任务时很少被阻塞,线程个数=CPU个数(并发数是确定的),线程数过多,会导致线程上下文切换,降低效率
    (2)I/O密集型任务: 执行任务时,可能会被I/O中断,即线程被挂起,所以线程个数>CPU个数,若线程少了,刚好又都被挂起,当有新任务来了,无法处理任务了
  • 当任务增加的时候能够动态的增加线程池中线程的数量直到达到一个阈值
    当任务执行完毕的时候,能够动态的销毁线程池中的线程
  • 该线程池的实现本质上也是生产者与消费者模型的应用。
    生产者线程向任务队列中添加任务,一旦队列有任务到来,如果有等待线程就唤醒来执行任务,如果没有等待线程并且线程数没有达到阈值,就创建新线程来执行任务。

2.线程池实现

  • 测试驱动开发,先将数据结构写好,再写接口,然后make一下看是否报错,最后再写实现
// 任务结构体,将任务放入队列由线程池中的线程来执行
typedef struct task
{
    void *(*run)(void *arg);    // 任务回调函数
    void *arg;                  // 回调函数参数
    struct task* next;//任务组织成链表的方式来保存
} task_t;

// 线程池结构体
typedef struct threadpool
{
    condition_t ready;  // 任务准备就绪或者线程池销毁通知
    task_t *first;      // 任务队列头指针
    task_t *last;       // 任务队列尾指针
    int counter;        // 线程池当前线程数
    int idle;           // 线程池中当前正在等待任务的线程数,处于空闲状态的线程个数
    int max_threads;    // 线程池中最大允许的线程数,线程池的阈值
    int quit;           // 销毁线程池的时候置1
} 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);
  • eg:线程池的实现
    NetworkProgramming-master (1)\NetworkProgramming-master\P41threadpool\threadpool.h
    NetworkProgramming-master (1)\NetworkProgramming-master\P41threadpool\threadpool.c
========================NetworkProgramming-master (1)\NetworkProgramming-master\P41threadpool\threadpool.h========
//
// Created by wangji on 19-8-15.
//

// p41 线程池

#ifndef NETWORKPROGRAMMING_THREADPOOL_H
#define NETWORKPROGRAMMING_THREADPOOL_H

#include "condition.h"

// 任务结构体,将任务放入队列由线程池中的线程来执行
typedef struct task
{
    void *(*run)(void *arg);    // 任务回调函数
    void *arg;                  // 回调函数参数
    struct task* next;//任务组织成链表的方式来保存
} task_t;

// 线程池结构体
typedef struct threadpool
{
    condition_t ready;  // 任务准备就绪或者线程池销毁通知
    task_t *first;      // 任务队列头指针
    task_t *last;       // 任务队列尾指针
    int counter;        // 线程池当前线程数
    int idle;           // 线程池中当前正在等待任务的线程数,处于空闲状态的线程个数
    int max_threads;    // 线程池中最大允许的线程数,线程池的阈值
    int quit;           // 销毁线程池的时候置1
} 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 //NETWORKPROGRAMMING_THREADPOOL_H

======NetworkProgramming-master (1)\NetworkProgramming-master\P41threadpool\threadpool.c==========
//
// Created by wangji on 19-8-15.
//

// p41 线程池

#include "threadpool.h"
#include <pthread.h>
#include <memory>
// #include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

//消费者
void *thread_routine (void *arg)
{
    struct timespec abstime;
    int timeout;//超时标记
    printf("thread 0x%x is starting\n", (int)pthread_self());//%x16进制打印
    threadpool_t *pool = (threadpool_t*)arg;
    while (1)
    {
        timeout = 0;
        condition_lock(&pool->ready);
        pool->idle++;
        // 等待队列有任务到来或者线程池销毁通知
        while (pool->first == NULL && !pool->quit)
        {
            printf("thread 0x%x is waiting\n", (int)pthread_self());
            //condition_wait(&pool->ready);

            //下面是带超时的contion_timewait
            clock_gettime(CLOCK_REALTIME, &abstime);
            abstime.tv_sec += 2; // 超时设置2秒
            int state = condition_timewait(&pool->ready, &abstime);
            if (state == ETIMEDOUT)
            {
                printf("thread 0x%x is wait time out\n", (int)pthread_self());
                timeout = 1;
                break;//超时了就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--;
            if (pool->counter == 0)//目的是销毁函数中的处于执行任务状态中的线程能够结束
            {
                condition_signal(&pool->ready);
            }
            condition_unlock(&pool->ready);// 跳出循环之前记得解锁
            break;
        }

        //超时break
        if (timeout && pool->first == NULL)
        {
            pool->counter--;
            condition_unlock(&pool->ready);// 跳出循环之前记得解锁
            break;
        }

        condition_unlock(&pool->ready);
    }

    printf("thread 0x%x is exiting \n", (int)pthread_self());//表示线程销毁了

    return NULL;
}

// 初始化线程池
void threadpool_init(threadpool_t *pool, int threads)
{
   // pool = (threadpool_t*)malloc(sizeof(pool));
   //对线程池中的各个字段初始化
    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)
{
    void *(*run)(void *arg);    // 任务回调函数
    void *arg;                  // 回调函数参数
    struct task* next;//任务组织成链表的方式来保存

    // 生成新任务
    task_t* newtask = (task_t *)malloc(sizeof(task_t));
    //cout << (char *)pool->first->arg << endl;
    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);
    pool->quit = 1;//1表示处于销毁状态
    if (pool->counter > 0)
    {
        if (pool->idle > 0)//判断正在处于等待中的线程
        {
            condition_broadcast(&pool->ready);
        }
        // 处于执行任务状态中的线程,不会收到广播
        // 线程池需要等执行任务状态中的线程全部退出
        while (pool->counter > 0)
        {
            condition_wait(&pool->ready);
        }
    }
    condition_unlock(&pool->ready);
    condition_destroy(&pool->ready);
    //free(pool);
}
  • eg:线程池的条件变量和互斥量的封装
    NetworkProgramming-master (1)\NetworkProgramming-master\P41threadpool\condition.h
    NetworkProgramming-master (1)\NetworkProgramming-master\P41threadpool\condition.c
===============NetworkProgramming-master (1)\NetworkProgramming-master\P41threadpool\condition.h==================
//
// Created by wangji on 19-8-15.
//

// p41 线程池

#ifndef NETWORKPROGRAMMING_CONDITION_H
#define NETWORKPROGRAMMING_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_timewait(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 //NETWORKPROGRAMMING_CONDITION_H
===================NetworkProgramming-master (1)\NetworkProgramming-master\P41threadpool\condition.c====================
//
// Created by wangji on 19-8-15.
//

#include "condition.h"
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

int condition_init(condition_t *cond)
{
    int state;
    state = pthread_cond_init(&(cond->pcond), NULL);
    if (state == 0)
    {
        return state;
    }
    state = pthread_mutex_init(&(cond->pmutex), NULL);
    if (state == 0)
    {
        return state;
    }
    return 0;
}

int condition_lock(condition_t *cond)
{
    //cout << pthread_mutex_lock(&(cond->pmutex)) << endl;
    if (pthread_mutex_lock(&(cond->pmutex)) == 0)
    {
        printf("pthread_mutex_lock error\n");
        exit(EXIT_FAILURE);
    }
    return 1;
}

int condition_unlock(condition_t *cond)
{
    pthread_mutex_unlock(&(cond->pmutex));
}
int condition_wait(condition_t *cond)
{
    return pthread_cond_wait(&(cond->pcond), &(cond->pmutex));
}

int condition_timewait(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 state;
    state = pthread_cond_destroy(&(cond->pcond));
    if (state == 0)
    {
        return state;
    }
    state = pthread_mutex_destroy(&(cond->pmutex));
    if (state == 0)
    {
        return state;
    }
    return 0;
}
  • NetworkProgramming-master (1)\NetworkProgramming-master\P41threadpool\test.c
//
// Created by wangji on 19-8-15.
//

// p41 线程池

#include "condition.h"
#include "threadpool.h"
#include <iostream>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

void *run (void *arg)
{
    printf("threadpool 0x%x working task %d\n", (int)pthread_self(), *(int*)(arg));
    sleep(1);
    free(arg);
    return NULL;
}

int main(void)
{
    // 结构体指针需要初始化 不晓得为啥报错
    threadpool_t pool;
    threadpool_init(&pool, 3);//初始化3个线程

    int i;
    for (i = 0; i < 10; ++i)//向线程池,添加10个任务
    {
        int *a = (int*)malloc(sizeof(int));
        *a = i;//传递动态内存,不会使得内部的某个指针指向i变量,因为for循环后i发生了改变,不要写成:threadpool_add_task(&pool, run, &i);
        threadpool_add_task(&pool, run, a);
    }
   // sleep(15);
    threadpool_destroy(&pool);//调用threadpool_destroy的主线程也要得到通知
    return 0;
}
  • 测试结果:
    在这里插入图片描述
    若将下面的代码屏蔽,程序则无法销毁
if (pool->counter == 0)//目的是销毁函数中的处于执行任务状态中的线程能够结束
{
    condition_signal(&pool->ready);
}

在这里插入图片描述

  • Makefile
.PHONY:clean
CC=gcc
CFLAGS=-Wall -g
ALL=main
OBJS=threadpool.o main.o condition.o
all:$(ALL)
%.o:%.c
	$(cc) $(CFLAGS) -c $< -o $@
main:$(OBJS)
	$(cc) $(CFLAGS) $^ -o $@ -lpthread -lrt
clean:
	rm -f $(ALL) *.o
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章