线程池实现

本文给出基于纯c的一个线程池的实现。

设计思路

其实线程池是在多线程的基础上开发的,原理也很简单,就是需要在处理任务之前把线程启动好。如果只写成一个main.c到也容易实现。线程池可以用线程句柄的数组来组织,事先启动好。需要一个任务队列,主线程向任务队列里面push任务,线程池中的线程从任务队列里面取任务。本质都是围绕一个任务队列展开。

如果在一个main.c里面实现,任务队列写成全局的即可。这样对于主线程和工作线程都很容易获取。

如果要封装的话,采用将线程数组和任务队列封装到一起的做法。此时,工作线程由于无法获得队列,所以,需要将任务队列的地址传进去。但是,由于任务队列已经被封装进线程池。所以,从逻辑上来说,后者是一个整体。传入,线程池的地址即可。当然,传入队列的地址也是可以的。

难点

线程池的关闭是一个难点,在队列为空的条件下。工作线程都阻塞在条件变量上。(这是一个很重要的认识,因为pthread_cond_wait阻塞线程之后会把锁释放,这样别的工作线程一样可以拿锁,从而全部阻塞在条件变量上)。关闭的时候,先关闭队列标志,然后唤醒全部阻塞在条件变量上的线程。他们判断此时队列的标志,会跳出while(1)的循环,从而结束。这样,pthread_join就可以回收资源。

代码

  • common.h
#ifndef COMMON_H_
#define COMMON_H_

#define TRUE 1
#define FALSE 0

typedef int Status;

typedef struct task {
    int left;
    int right;
}task_t;

#endif
  • threadpool.h
#ifndef THREADPOOL_H_
#define THREADPOOL_H_
#include <pthread.h>
#include "common.h"
#include "threadpool_queue.h"

typedef void* (*handle_t)(void*);

typedef struct threadpool {
    pthread_t* pool_arr;
    int pool_num;
    handle_t pool_handle;

    que_t pool_que;

}threadpool_t;

void threadpool_init( threadpool_t* ppool, int thread_num, handle_t thread_handle, int que_size  );
void threadpool_destroy( threadpool_t* ppool );

void threadpool_start( threadpool_t* ppool );
void threadpool_stop( threadpool_t* ppool );

void threadpool_put( threadpool_t* ppool, task_t* val );
void threadpool_get( threadpool_t* ppool, task_t* val );

#endif
  • threadpool.c
#include "threadpool.h"
#include "threadpool_queue.h"
#include <assert.h>
#include <stdlib.h>

void threadpool_init( threadpool_t* ppool, 
                      int thread_num, 
                      handle_t thread_handle, 
                      int que_size) {
    assert( ppool && thread_num>0 && thread_handle && que_size > 0 );

    ppool->pool_arr = (pthread_t*)malloc( sizeof(pthread_t) * thread_num );
    assert( ppool->pool_arr );

    ppool->pool_num = thread_num;
    ppool->pool_handle = thread_handle;

    que_init( &(ppool->pool_que), que_size );
}// threadpool_init

void threadpool_destroy( threadpool_t* ppool ) {
    assert( ppool );

    free(ppool->pool_arr);

    que_destroy( &(ppool->pool_que) );

}// threadpool_destroy

void threadpool_start( threadpool_t* ppool ) {
    int i = 0;
    assert( ppool );

    que_start( &(ppool->pool_que) );
    for( i = 0; i < ppool->pool_num; ++i ){
        if( pthread_create( ppool->pool_arr + i, NULL, ppool->pool_handle, (void*)ppool ) != 0 ) {
            perror("pthread_create");
            exit(EXIT_FAILURE);
        }
    }

}// threadpool_start

void threadpool_stop( threadpool_t* ppool ) {
    int i = 0;
    assert( ppool );

    que_stop( &(ppool->pool_que) );
    que_wake_all( &(ppool->pool_que) );
    for( i = 0; i < ppool->pool_num; ++i ) {
        if( pthread_join( ppool->pool_arr[i], NULL ) != 0 ) {
            perror("pthread_join");
            exit(EXIT_FAILURE);
        }
    }
}// threadpool_stop

void threadpool_put( threadpool_t* ppool, task_t* val ) {
    assert( ppool );
    que_push( &(ppool->pool_que), val );
}// threadpool_put

void threadpool_get( threadpool_t* ppool, task_t* val ) {
    assert( ppool );
    que_pop( &(ppool->pool_que), val );
}// threadpool_get
  • threadpool_queue.h
    这一部分做了较大的修改,主要是增加了队列的有效标志。以及两个start和stop函数,用来配合线程池里面的start,stop函数。wake_all函数也是用来配合线程池里面的stop函数。
#ifndef THREADPOOL_QUEUE_H_
#define THREADPOOL_QUEUE_H_
#include <pthread.h>
#include "common.h"

typedef struct que {
    task_t* que_arr;
    int que_front;
    int que_rear;
    int que_size;
    pthread_mutex_t que_mtx;
    pthread_cond_t que_cond_pro; // que full block
    pthread_cond_t que_cond_con; // que empty block
    int que_flag;
}que_t;

void que_init( que_t* pque, int size );
void que_destroy( que_t* pque );

void que_push( que_t* pque, task_t* val );
void que_pop( que_t* pque, task_t* val );

Status que_empty( que_t* pque ); // return true iff que is empty
Status que_full( que_t* pque );  // return true iff que is full

void que_start( que_t* pque );
void que_stop( que_t* pque );

void que_wake_all( que_t* pque );
Status que_valid( que_t* pque );

#endif
  • threadpool_queue.c
#include "threadpool_queue.h"
#include <assert.h>
#include <stdlib.h> 
#include <errno.h>

void que_init( que_t* pque, int size ) {
    assert(pque && size > 0);

    pque->que_arr = (task_t*)malloc( size * sizeof(que_t) );
    assert(pque->que_arr);

    pque->que_size = size;

    pque->que_front = pque->que_rear = 0;

    if( ( pthread_mutex_init( &(pque->que_mtx) , NULL ) ) != 0 ){
        perror("pthread_mutex_init");
        exit(EXIT_FAILURE);
    }

    if( ( pthread_cond_init( &(pque->que_cond_pro), NULL ) ) != 0 ){
        perror("pthread_cond_init");
        exit(EXIT_FAILURE);
    }
    if( ( pthread_cond_init( &(pque->que_cond_con), NULL ) ) != 0 ){
        perror("pthread_cond_init");
        exit(EXIT_FAILURE);
    }

    pque->que_flag = 0;
}// que_init

void que_destroy( que_t* pque ) {
    assert(pque);

    assert(pque->que_arr);
    free(pque->que_arr);

    if( ( pthread_mutex_destroy( &(pque->que_mtx) ) ) != 0 ) {
        perror("pthread_mutex_destroy");
        exit(EXIT_FAILURE);
    }
    if( ( pthread_cond_destroy( &(pque->que_cond_pro) ) ) != 0 ) {
        perror("pthread_mutex_destroy");
        exit(EXIT_FAILURE);
    }
    if( ( pthread_cond_destroy( &(pque->que_cond_con) ) ) != 0 ) {
        perror("pthread_mutex_destroy");
        exit(EXIT_FAILURE);
    }
}// que_destroy

void que_push( que_t* pque, task_t* val ) {
    assert(pque && val);

    pthread_mutex_lock( &(pque->que_mtx) );
    while( que_full(pque) && que_valid(pque) ) {
        pthread_cond_wait( &(pque->que_cond_pro), &(pque->que_mtx) );
    }

    if( que_valid(pque) ) {

        pque->que_arr[pque->que_rear] = *val;
        pque->que_rear = (pque->que_rear + 1) % pque->que_size;

        pthread_cond_signal(&(pque->que_cond_con));
    }

    pthread_mutex_unlock( &(pque->que_mtx) );
}// que_push

void que_pop( que_t* pque, task_t* val ) {
    assert(pque && val );

    pthread_mutex_lock( &(pque->que_mtx) );

    while( que_empty(pque) && que_valid(pque) ) {
        pthread_cond_wait( &(pque->que_cond_con), &(pque->que_mtx) );
    }

    if( que_valid(pque) ) {
        *val = pque->que_arr[pque->que_front];
        pque->que_front = (pque->que_front + 1) % pque->que_size;
        pthread_cond_signal(&(pque->que_cond_pro));
    }

    pthread_mutex_unlock( &(pque->que_mtx) );
}// que_pop

Status que_empty( que_t* pque ) {
    assert(pque);
    return pque->que_front == pque->que_rear;
}// que_empty

Status que_full( que_t* pque ) {
    assert(pque);
    return (pque->que_rear + 1) % pque->que_size == pque->que_front;
}// que_full

void que_start( que_t* pque ) {
    assert(pque);
    pque->que_flag = 1;
}// que_start

void que_stop( que_t* pque ) {
    assert(pque);
    pque->que_flag = 0;
}// que_stop

void que_wake_all( que_t* pque ) {
    assert( pque );
    pthread_cond_broadcast( &(pque->que_cond_con) );
    pthread_cond_broadcast( &(pque->que_cond_pro) );
}// que_wake_all

Status que_valid( que_t* pque ) {
    assert(pque);
    return pque->que_flag;
}// que_valid
  • main.c
    最后的主函数,用select监听键盘输入。任务就是计算两个数的和。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <assert.h>
#include "threadpool.h"
#define LEN 128

static void* worker(void*);
static int process_task( task_t* val );

int main( int argc, char* argv[] ){

    fd_set readfds;
    fd_set tmpfds;
    struct timeval tv;
    int ret = 0;
    int nread = 0;
    char buf[LEN];

    if( argc != 3 ){
        fprintf( stderr, "Usage: argv[1] is thread num, argv[2] is que size!\n" );
        exit(EXIT_FAILURE);
    }

    threadpool_t pool;
    threadpool_init( &pool, atoi(argv[1]), worker, atoi(argv[2]) );
    threadpool_start( &pool );

    FD_ZERO(&readfds);
    FD_SET(STDIN_FILENO, &readfds);

    while(1) {
        tmpfds = readfds;
        tv.tv_sec = 3;
        tv.tv_usec = 0;

        ret = select( STDIN_FILENO+1, &tmpfds, NULL, NULL, &tv );

        if( -1 == ret ) {
            perror( "select" );
            exit( EXIT_FAILURE );
        }
        else if( !ret ) {
            printf( "No input data in 3 seconds!\n" );
        }
        else {
            if( FD_ISSET(STDIN_FILENO, &tmpfds) ) {
                memset(buf, 0, sizeof(buf));
                nread = read( STDIN_FILENO, buf, LEN-1 );
                if( -1 == nread ) {
                    perror( "read" );
                    exit( EXIT_FAILURE );
                }
                if( !nread ) break;
                else{
                    buf[nread-1] = 0;
                    task_t val;
                    sscanf(buf, "%d %d", &val.left, &val.right);

                    threadpool_put( &pool, &val );
                }
            }
        }
    }
    threadpool_stop( &pool );
    threadpool_destroy( &pool );
    exit(EXIT_SUCCESS);

}// main

static void* worker(void* arg) {
    assert(arg);

    threadpool_t* ppool = (threadpool_t*)(arg);
    task_t val;

    while( 1 ){
        threadpool_get( ppool, &val );
        if( !que_valid( &(ppool->pool_que) ) ) break;
        int res = process_task(&val);
        printf( "%lu execute the task: %d\n", pthread_self(), res );
        sleep(1);
    }
    pthread_exit(NULL);
}// thread_handle

static int process_task( task_t* val ) {
    return val->left + val->right;
}// process_task
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值