Linux 通用线程池的实现

 

用百度搜索一下“Linux 线程池”,很出现很多个结果,有的提供的源程序是可以使用的,但仔细分析后,发现其设计结构的并不太合理。线程池的设计有两个难点。1、在池中线程完成一 个任务后自动阻塞,并等待被唤醒。2、池中的空闲线程数会随着任务的多少而有一个动态的变化,如任务多而空闲线程过少时,程序会创建新的线程来补充;当任务少而空闲线程过多时,程序会取消一些空闲线程以节约系统资源。Linux系统的一个进程最多支持2024个线程。

   一些网络服务器在单位时间内需要处理大量的连接请求,但服务的时间去很短。使用普通的方法,即接收一个服务请求,创建一个服务线程方法来提供服务,会浪费系统很多资源。因为线程的创建和销毁己经占用了大量的CPU。而使用线程池方法能够很好的解决这一问题。

   线程池采用预创建技术,在应用程序启动后,立即创建一定数量的线程,并让这些线程处于阻塞状态,(即空闲线程)不消耗CPU,只占用较小的内存空间。当任 务来临时,应用程序即唤醒一个空闲的线程,并处理此任务。当此线程处理完任务后,重新回到阻塞状态,并不退出。当系统比较空闲时,大部分线程处于空闲状 态,线程池会自动销毁一些线程,回收系统资源。

   下面一个线程池构架。不同于一些网友对线程池的设计,此构架更为简单。程序是根据一网络上现有程序修改的,链接地址:http://cppthreadpool.googlecode.com/svn/trunk/,有兴趣的话可以自己去看源代码,在这里不做过多介绍。

   线程池共有两个类,WorkThread 和 ThreadPool。WorkThread 是工作线程类,即线程池中线程所需执行的任务。当有一个新的WorkerThread对象到达时,即有一个处于阻塞状态的线程被唤醒。

class WorkerThread{
public:
    int id;

    unsigned virtual executeThis()

    {
        return 0;
    }

    WorkerThread(int id) : id(id) {}
    virtual ~WorkerThread(){}
};

    ThreadPool类负责对线程池进行管理,包括:创建新线程、线程间的同步、把新的任务加入到工作队列中、让池中的线程数动态的改变。其类声明如下:

class ThreadPool{
public:
    ThreadPool();
    ThreadPool(int maxThreadsTemp);
    virtual ~ThreadPool();

    void destroyPool(int maxPollSecs);

    bool assignWork(WorkerThread *worker);
    bool fetchWork(WorkerThread **worker);

    void initializeThreads();
 
    static void *threadExecute(void *param);

    static pthread_mutex_t mutexSync;
    static pthread_mutex_t mutexWorkCompletion;
   
protected:
    static void    MoveToBusyList(pthread_t id);   //move and idle thread to busy thread
    static void    MoveToIdleList(pthread_t id);

 

    void CreateIdleThread(int num);
    void DeleteIdleThread(int num);

 

    pthread_mutex_t m_VarMutex;        //mutex for var

    pthread_mutex_t mutexThreadList;    

    static pthread_mutex_t mutexBusyList;
    static pthread_mutex_t mutexIdleList;

 

private:
    unsigned int m_InitNum;

    unsigned int m_MaxNum;
    unsigned int m_AvailLow;
    unsigned int m_AvailHigh;
    //unsigned int m_AvailNum;


    sem_t availableWork;
    //sem_t availableThreads;

 

    vector<pthread_t>   m_ThreadList; //Thread List 
    static vector<pthread_t>   m_BusyList;  //Busy List

    static vector<pthread_t>   m_IdleList;  //Idle List

    vector<WorkerThread *> workerQueue;

    //int topIndex;
    //int bottomIndex;
 
    int incompleteWork;    //number of works be done

   
    int queueSize;    //the size of workQueue

};

    在源程序上进行了适当的修改,使得池中的空闲线程数可以动态的变化。创建一个线程后,立即此线程的ID存入 m_ThreadList 和 m_IdleList 队列中,通过调用fetchWork函数将自己置于阻塞状态。当workerQueue中没有任务等待执行,fetchWork执行sem_wait()即处于阻塞状态。当有新的任务加入到 workerQueue 中,availableWork 信号执行一次 sem_post 操作,唤醒一个空闲线程。线程被唤醒后,其ID号被添加到 m_BusyList队列中,执行完任务后,ID号在 m_BusyList 队列中被删除,再添加到 m_IdleList 队列中。

    应用程序所创建的线程总数不能超过m_MaxNum,池中的线程总数不高于m_MaxNum时,空闲线程维持在 m_AvailLow 之上,同样,当空闲线程数高于m_AvailHigh时,程序会自动销毁一些线程,使空闲线程数目维持在一个合理的范围内。

    调用的实现方法如下:

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

    ThreadPool* myPool = new ThreadPool(20);
    myPool->initializeThreads();

    //We will count time elapsed after initializeThreads()
    time_t t1=time(NULL);

    //Lets start bullying ThreadPool with tonnes of work !!!
    for(unsigned int i=0;i<ITERATIONS;i ){
        SampleWorkerThread* myThread = new SampleWorkerThread(i);
        cout << "myThread[" << myThread->id << "] = [" << myThread << "]" << endl;
        myPool->assignWork(myThread);
    }
 
    sleep(10);

    for(unsigned int i=0;i<ITERATIONS;i ){
        SampleWorkerThread* myThread = new SampleWorkerThread(i);
        cout << "myThread[" << myThread->id << "] = [" << myThread << "]" << endl;
        myPool->assignWork(myThread);
    }

    sleep(10);
    myPool->destroyPool(2);

    time_t t2=time(NULL);
    cout << t2-t1 << " seconds elapsed\n" << endl;
    delete myPool;
 
    return 0;
}

    通过这个测试程序,我们可以看到池中的线程数动态的变化。

 

    ThreadPool的实现代码过长,受篇幅限制不便于发表,如有需要可以留言。

posted @ 2012-11-09 10:16  孤火  阅读(408)  评论(0编辑  收藏  举报