LINUX under C thread pool to achieve "reprint"

Reprinted from the original: https://blog.csdn.net/hubi0952/article/details/8045094

1. The basic principle of the thread pool

  In the conventional server configuration, there is often a general listening thread monitor has no new user connects to the server whenever a new user enters, the server open a new user thread processing the user data packet. This thread serves only the user, the user and the server When the connection is closed, the server side to destroy this thread. However, frequently opened and destroying threads greatly occupied system resources. And in the case of large number of users, in order to open up the system and destroying threads will waste a lot of time and resources. Thread Pool provides contradiction a solution outside the large number of users and server resources are limited, the thread pool and traditional a user corresponding to different treatment methods a thread, its basic idea is to open up some threads in memory when the program begins, thread the number is fixed, they form a class alone, shielding the external operation, while a packet to the server only needs to thread pool on it. When a new client request arrives, not by creating a new thread for their services, choosing instead to a free thread from the "pool" for new customer service requests, the service is completed, the thread enters an idle thread pool. If there is no thread is idle, then the packet will be temporarily accumulated and then processed there waiting thread pool idle threads later. By reusing existing thread object for multiple tasks, reduces the thread object creation and destruction overhead. When a client requests, the thread object already exists, it can improve the response time of the request, thereby improving the overall performance of the system services.

  In general the realization of a thread pool includes the following components:

1) the thread manager: to create and manage the thread pool.

2) worker threads: thread actually perform the task thread pool. At initialization thread will be created before the fixed number of threads in the pool, these initialization thread usually in the idle state, generally do not take up CPU, take up less memory space.

3) task interface: each task must implement the interface when the thread pool task queue executable tasks are idle worker thread to perform free and busy tone (by mutex thread is achieved, with the front setting a flag bit in the article about the same), to form the abstract interface task, the thread pool can be done with specific tasks unrelated.

4) task queue: used to store the task is not processed, there is provided a buffer mechanism, this structure there are several methods commonly used is the queue, the main application of the FIFO principle, another is a data list or the like structure, it can be dynamically allocated memory space, more flexible applications, the list below is used.

The following does not repeat Baidu "application thread pool technology in concurrent server" is written in great detail!

Transfer: http://blog.csdn.net/zouxinfox/article/details/3560891

  When do I need to create a thread pool it? Simply put, if an application requires frequent creating and destroying threads, and task execution time and very short, so that thread creation and destruction of the overhead can not be ignored, but also the opportunity at this time of the appearance of the thread pool. If the thread creation and destruction of time compared to the task execution time is negligible, it is not necessary to use a thread pool.

    Here is a thread pool under Linux system with C language created. Thread pool maintains a task list (each CThread_worker structure is a task).
    pool_init () function previously created max_thread_num threads, each thread execute thread_routine () function. The function

  1.  
    1. while (pool->cur_queue_size == 0)
    2. {
    3.       pthread_cond_wait (&(pool->queue_ready),&(pool->queue_lock));
    4. }

He said that if the task is not the task list, the thread is blocked waiting for the state. Otherwise, remove the task from the queue and executed.
    
    pool_add_worker () function to add a task to the task list thread pool in, (& (pool-> queue_ready) ) wake up a thread for the blocking state (if any) after the addition by calling pthread_cond_signal.
    
    pool_destroy () function is used to destroy the thread pool, thread pool task list of tasks will not be executed, but the thread running the task will always run after their exit.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <assert.h>
 
/*
*线程池里所有运行和等待的任务都是一个CThread_worker
*由于所有任务都在链表里,所以是一个链表结构
*/
typedef struct worker
{
    /*回调函数,任务运行时会调用此函数,注意也可声明成其它形式*/
    void *(*process) (void *arg);
    void *arg;/*回调函数的参数*/
    struct worker *next;
 
} CThread_worker;
 
 
 
/*线程池结构*/
typedef struct
{
    pthread_mutex_t queue_lock;
    pthread_cond_t queue_ready;
 
    /*链表结构,线程池中所有等待任务*/
    CThread_worker *queue_head;
 
    /*是否销毁线程池*/
    int shutdown;
    pthread_t *threadid;
    /*线程池中允许的活动线程数目*/
    int max_thread_num;
    /*当前等待队列的任务数目*/
    int cur_queue_size;
 
} CThread_pool;
 
 
 
int pool_add_worker (void *(*process) (void *arg), void *arg);
void *thread_routine (void *arg);
 
 
//share resource
static CThread_pool *pool = NULL;
void
pool_init (int max_thread_num)
{
    pool = (CThread_pool *) malloc (sizeof (CThread_pool));
 
    pthread_mutex_init (&(pool->queue_lock), NULL);
    pthread_cond_init (&(pool->queue_ready), NULL);
 
    pool->queue_head = NULL;
 
    pool->max_thread_num = max_thread_num;
    pool->cur_queue_size = 0;
 
    pool->shutdown = 0;
 
    pool->threadid = (pthread_t *) malloc (max_thread_num * sizeof (pthread_t));
    int i = 0;
    for (i = 0; i < max_thread_num; i++)
    { 
        pthread_create (&(pool->threadid[i]), NULL, thread_routine,NULL);
    }
}
 
 
 
/*向线程池中加入任务*/
int
pool_add_worker (void *(*process) (void *arg), void *arg)
{
    /*构造一个新任务*/
    CThread_worker *newworker = (CThread_worker *) malloc (sizeof (CThread_worker));
    newworker->process = process;
    newworker->arg = arg;
    newworker->next = NULL;/*别忘置空*/
 
    pthread_mutex_lock (&(pool->queue_lock));
    /*将任务加入到等待队列中*/
    CThread_worker *member = pool->queue_head;
    if (member != NULL)
    {
        while (member->next != NULL)
            member = member->next;
        member->next = newworker;
    }
    else
    {
        pool->queue_head = newworker;
    }
 
    assert (pool->queue_head != NULL);
 
    pool->cur_queue_size++;
    pthread_mutex_unlock (&(pool->queue_lock));
    /*好了,等待队列中有任务了,唤醒一个等待线程;
    注意如果所有线程都在忙碌,这句没有任何作用*/
    pthread_cond_signal (&(pool->queue_ready));
    return 0;
}
 
 
 
/*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
把任务运行完后再退出*/
int
pool_destroy ()
{
    if (pool->shutdown)
        return -1;/*防止两次调用*/
    pool->shutdown = 1;
 
    /*唤醒所有等待线程,线程池要销毁了*/
    pthread_cond_broadcast (&(pool->queue_ready));
 
    /*阻塞等待线程退出,否则就成僵尸了*/
    int i;
    for (i = 0; i < pool->max_thread_num; i++)
        pthread_join (pool->threadid[i], NULL);
    free (pool->threadid);
 
    /*销毁等待队列*/
    CThread_worker *head = NULL;
    while (pool->queue_head != NULL)
    {
        head = pool->queue_head;
        pool->queue_head = pool->queue_head->next;
        free (head);
    }
    /*条件变量和互斥量也别忘了销毁*/
    pthread_mutex_destroy(&(pool->queue_lock));
    pthread_cond_destroy(&(pool->queue_ready));
    
    free (pool);
    /*销毁后指针置空是个好习惯*/
    pool=NULL;
    return 0;
}
 
 
 
void *
thread_routine (void *arg)
{
    printf ("starting thread 0x%x\n", pthread_self ());
    while (1)
    {
        pthread_mutex_lock (&(pool->queue_lock));
        /*如果等待队列为0并且不销毁线程池,则处于阻塞状态; 注意
        pthread_cond_wait是一个原子操作,等待前会解锁,唤醒后会加锁*/
        while (pool->cur_queue_size == 0 && !pool->shutdown)
        {
            printf ("thread 0x%x is waiting\n", pthread_self ());
            pthread_cond_wait (&(pool->queue_ready), &(pool->queue_lock));
        }
 
        /*线程池要销毁了*/
        if (pool->shutdown)
        {
            /*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/
            pthread_mutex_unlock (&(pool->queue_lock));
            printf ("thread 0x%x will exit\n", pthread_self ());
            pthread_exit (NULL);
        }
 
        printf ("thread 0x%x is starting to work\n", pthread_self ());
 
        /*assert是调试的好帮手*/
        assert (pool->cur_queue_size != 0);
        assert (pool->queue_head != NULL);
        
        /*等待队列长度减去1,并取出链表中的头元素*/
        pool->cur_queue_size--;
        CThread_worker *worker = pool->queue_head;
        pool->queue_head = worker->next;
        pthread_mutex_unlock (&(pool->queue_lock));
 
        /*调用回调函数,执行任务*/
        (*(worker->process)) (worker->arg);
        free (worker);
        worker = NULL;
    }
    /*这一句应该是不可达的*/
    pthread_exit (NULL);
}
 
//    下面是测试代码
 
void *
myprocess (void *arg)
{
    printf ("threadid is 0x%x, working on task %d\n", pthread_self (),*(int *) arg);
    sleep (1);/*休息一秒,延长任务的执行时间*/
    return NULL;
}
 
int
main (int argc, char **argv)
{
    pool_init (3);/*线程池中最多三个活动线程*/
    
    /*连续向池中投入10个任务*/
    int *workingnum = (int *) malloc (sizeof (int) * 10);
    int i;
    for (i = 0; i < 10; i++)
    {
        workingnum[i] = i;
        pool_add_worker (myprocess, &workingnum[i]);
    }
    /*等待所有任务完成*/
    sleep (5);
    /*销毁线程池*/
    pool_destroy ();
 
    free (workingnum);
    return 0;
}

 

 

Published 22 original articles · won praise 9 · views 8821

Guess you like

Origin blog.csdn.net/ljm_c_bok/article/details/88800889