Linux程序设计之线程池(C语言实现)

Linux程序设计之线程池(C语言实现)

首先介绍Linux下线程的基础编程知识。

创建线程

#include<pthread.h>

int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

函数用于创建一个新线程,调用成功返回值为0,如果失败则返回错误代码。与fork创建进程不同的是,fork后,父子进程将在同一位置继续执行下去,只是fork调用的返回值是不同的;但对于新线程来说,我们必须明确地提供给它一个函数指针,新线程将在这个新位置开始执行。

参数说明:

  • thread:指向pthread_t类型数据的指针。线程被创建时,这个指针指向的变量中将被写入一个标识符,我们用该标识符来引用新线程。
  • attr:用于设置线程的属性,一般设置为NULL。(更多请参考文档)
  • start_routine:一个指向以void的指针为参数,返回值也是void的指针的函数指针。可以传递一个任一类型的参数并返回一个任一类型的指针。
  • arg:传递给该函数的参数。

等待线程

#include<pthread.h>

int pthread_join(pthread_t th, void **thread_return);

参数说明:

  • th:指定了将要等待的线程
  • thread_return:指向线程返回值指针的指针

结束线程

#include<pthread.h>

int pthread_exit(void *retval);

线程通过调用pthread_exit函数终止执行,就如同进程在结束时调用exit函数一样。这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。需要注意的是,绝对不能用它来返回一个指向局部变量的指针,因为线程调用该函数后,这个局部变量就不存在了,这将引起严重的程序bug。

线程互斥量

#include<pthread.h>

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

初始化一个互斥量,第二个参数一般设置为NULL。

#include<pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

尝试锁定互斥量。如果程序试图对一个已经加了锁的互斥量调用pthread_mutex_lock,程序就会被阻塞。

#include<pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

解锁互斥量

#include<pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

释放互斥量

线程条件变量

#include<pthread.h>
int pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *cattr);

初始化一个条件变量,第二个参数一般为NULL。

#include<pthread.h>

int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex);

解锁mutex函数指向的互斥锁,并使当前线程阻塞在cv参数指向的条件变量上。被阻塞的线程可以被pthread_cond_signal函数、pthread_cond_broadcast函数唤醒,也可能在被信号中断后被唤醒。

注意,当pthread_cond_wait函数返回时,相应的互斥锁将被当前线程锁定,即使函数出错返回。

#include<pthread.h>

int pthread_cond_signal(pthread_cond_t *cv);

唤醒被阻塞在指定条件变量上的一个线程。

#include<pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cv);

唤醒所有被阻塞在某个条件变量上的线程。

#include<pthread.h>
#include<time.h>

int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mp, const structtimespec *abstime);

解锁mutex函数指向的互斥锁,并使当前线程阻塞在cv参数指向的条件变量上,到一定时间即使条件未发生也会解除阻塞。

#include<pthread.h>

int pthread_cond_destroy(pthread_cond_t *cv);

释放条件变量cv


基础知识已经准备好了,现在我们来写线程池吧。

实现细节都在代码以及注释里面了。

pthreadpool.h

#include<pthread.h>
#include<semaphore.h>

typedef void *(*thread_handler_t)(void *);
typedef struct{
    
    
    thread_handler_t handler;           //函数指针
    void *arg;                          //参数
}task_t;


typedef struct{
    
    
    pthread_t *threads;                 //线程数组
    task_t *task_queue;                 //任务队列
    int threads_num;                
    int stop;
    int queue_size;                     //这里使用的是数组来实现循环队列,queue_size是数组大小,实际队列可用的容量是size-1
    int queue_front;                    //指向队列头部
    int queue_end;                      //指向队列尾部

    //sem_t tasks_num;
    pthread_mutex_t mutex;              //任务队列锁
    pthread_cond_t cv;                  //线程变量

}pthreadpool;

int createpool(pthreadpool *pool, int thread_num, int task_queue_size);     //创建线程池
int append(pthreadpool *pool, task_t t);                                    //往线程池中添加任务
int destroypool(pthreadpool *pool);                                         //销毁线程池
void *empty_task(void *threadpool);                                         //每个线程负责执行任务的函数        

pthreadpool.c

#include<stdlib.h>
#include<stdio.h>
#include"pthreadpool.h"

int createpool(pthreadpool *pool, int thread_num, int task_queue_size){
    
    
    pool->stop = 0;
    pool->threads_num = thread_num;
    pool->threads = (pthread_t *)malloc(sizeof(pthread_t)*thread_num);
    if(!pool->threads){
    
    
        printf("malloc threads memory failed.\n");
        return -1;
    }
    int ret;
    for(int i=0;i<thread_num;i++){
    
    
       ret = pthread_create(&pool->threads[i], NULL, &empty_task, pool); 
       if(ret != 0){
    
    
           printf("pthread create failed.\n");
           return -1;
       }
    }

    pool->task_queue = (task_t *)malloc(sizeof(task_t)*task_queue_size);
    if(!pool->task_queue){
    
    
        printf("malloc tasks queue memory failed.\n");
        return -1;
    }
    
    pool->queue_size = task_queue_size;
    pool->queue_front = 0;
    pool->queue_end = 0;
    /*
    ret = sem_init(&pool->tasks_num,0,0);
    if(ret != 0){
        printf("sem init error.\n");
        return -1;
    }
    */
    ret = pthread_cond_init(&pool->cv, NULL);
    if(ret != 0){
    
    
        printf("pthread cond init failed.\n");
        return -1;
    }

    ret = pthread_mutex_init(&pool->mutex, NULL);
    if(ret != 0){
    
    
        printf("mutex init error.\n");
        return -1;
    }

    printf("create pthread pool success.\n");
    return 1;
}

int destroypool(pthreadpool *pool){
    
    
    pool->stop = 1;
    /*
    for(int i=0;i<pool->threads_num;i++){           //唤醒所有线程
        sem_post(&pool->tasks_num);             
    }
    */
    pthread_cond_broadcast(&pool->cv);             //唤醒所有线程   
    for(int i=0;i<pool->threads_num;i++){
    
    
        pthread_join(pool->threads[i], NULL);      //等待所有线程结束
    }
    free(pool->threads);                            //释放空间
    free(pool->task_queue);
    //sem_destroy(&pool->tasks_num);
    pthread_cond_destroy(&pool->cv);
    pthread_mutex_destroy(&pool->mutex);     
    printf("destory pthread pool success...\n");       
    return 1;
}

int append(pthreadpool *pool, task_t t){
    
    
    pthread_mutex_lock(&pool->mutex);
    if((pool->queue_end+1) % pool->queue_size == pool->queue_front){
    
        //队列已满
        printf("task queue full...\n");
        pthread_mutex_unlock(&pool->mutex);
        return -1;
    }
    pool->task_queue[pool->queue_end] = t;                              //任务入队
    pool->queue_end = (pool->queue_end+1)%pool->queue_size;
    pthread_mutex_unlock(&pool->mutex);
    pthread_cond_signal(&pool->cv);                                     //唤醒消费者线程
    return 1;
}

void *empty_task(void *threadpool){
    
    
    pthreadpool *pool = (pthreadpool *)threadpool;

    while(1){
    
    
        //sem_wait(&pool->tasks_num);
        pthread_mutex_lock(&pool->mutex);                   //从任务队列中获取任务时,先将任务队列上锁
        while(pool->queue_front == pool->queue_end){
    
            //若任务队列为空,则应该一直阻塞
            pthread_cond_wait(&pool->cv, &pool->mutex);
            if(pool->stop) break;
        }
        if(pool->stop){
    
                                         //收到线程结束信号
            pthread_mutex_unlock(&pool->mutex); 
            break;
        }
        //pthread_mutex_lock(&pool->mutex);
        task_t t = pool->task_queue[pool->queue_front];     //任务出队
        pool->queue_front = (pool->queue_front+1) % pool->queue_size;
        pthread_mutex_unlock(&pool->mutex);                 //解锁任务队列
        *(t.handler)(t.arg);
    }
    pthread_exit(NULL);
}

使用示例:

#include<unistd.h>
#include<stdlib.h>
#include"pthreadpool.h"

int func(int id){
    
    
    int t = rand()%1000+1;
    usleep(t*1000);
    printf("Task id: %d time: %d\n", id, t);
    return id;
}

int main(){
    
    
    pthreadpool *pool = (pthreadpool*)malloc(sizeof(pthreadpool));
    createpool(pool, 20, 100);
    task_t t;
    t.handler = &func;
    for(int i=0;i<50;i++){
    
    
        t.arg = (void*)i;
        append(pool, t);
    }
    sleep(10);
    destroypool(pool);
    return 0;
}

使用gcc编译的时候记得加上-lpthread参数。

猜你喜欢

转载自blog.csdn.net/qq_38600065/article/details/108555978
今日推荐