线程池的实现设计

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程池的实现设计相关的知识,希望对你有一定的参考价值。

线程池的实现原理
线程池的概念很简单,就是new一堆线程,当有任务到来时,抓一个线程去执行,执行完之后再丢回线程池。省去了新建和注销线程的开销。
线程池工作分为以下几步:
(1)创建线程固定数目的线程(如:20个)
并让线程挂起等待任务
(2)给某个线程设置任务
(3)激活该线程,让其执行任务
(4)线程执行任务完毕后,回收该线程

虽然过程不复杂,但是实现过程中的逻辑却不简单,以darwin的源码为例:

类结构如下:(伪代码)

class ThreadTask //是一个执行任务的线程类
{
friend class ThreadPool;
public:
    Int start();       //创建线程
static void _Entry(void *inThread);   //该线程的回调函数
virtual void Entry();
Bool Active();     //激活该线程

private:
Task *m_task;

OSCond     m_Cond_Run;    //用于检测线程是否运行的条件变量
OSMutex     m_Mutex_Run;   //相对应的互斥量

OSCond     m_Cond_IsTaskRun;  //用于检测是否运行任务的条件变量
OSMutex     m_Mutex_IsTaskRun; //相对应的互斥量

};

class Task
{
friend class ThreadTask;
public:
    Bool RunTask(){ ThreadPool::RegistTask(this);}
virtual int Run()=0; //最终执行任务的函数
};

class ThreadPool
{
friend class ThreadTask;
public:
    ThreadPool(int n);   //初始化
static  void RegistTask(Task* ptask); //注册任务
private:
    List<ThreadTask*> m_useList;
    List<ThreadTask*> m_unUseList;
};

类的实现如下:(伪代码)

class MyTask :public Task
{
    int Run(){...};   //代码中,只要实现Run()函数,就可以了。
};
int main()
{
    MyTask *mytask=new MyTask();
    mytask->RunTask();   //调用RunTask()就可以是任务运行,但是一般通常把这个函数封装起来而不这样用
    return 0;
}

//取一个线程执行任务
void ThreadPool::RegistTask(Task* ptask)
{
    if(m_unUseList.empty())
    {
        //如果没有空闲的线程,那么就创建一个线程
        ThreadTask *pthread = new ThreadTask();
        pthread->start();
        m_useList.Push(pthread);
    }
    ThreadTask *pthread = m_unUseList.Pop();
    p->m_task=ptask;//给线程添加任务
    pthread->Active();//激活线程
}
//初始化n个线程,并激活
void ThreadPool::ThreadPool(int n)
{
    while(n--)
    {
        ThreadTask *pthread = new ThreadTask();
        pthread->start();
        m_useList.Push(pthread);
    }
}
//创建线程
Int ThreadTask::start()
{
    //可以看出线程的回调函数是_Entry实际上是一个静态函数,目的是为了调用各自进程的Entry()函数
    return pthread_create((pthread_t*)&m_ThreadID, &m_ThreadAttr, _Entry, (void*)this);
}
void ThreadTask::_Entry(void *inThread)
{
    //调用当前进程对应的Entry()函数
    inThread->Entry();
}
void ThreadTask::Entry()
{
    while (1)
    {
        //这一段相当于P(m_Cond_Run)
        m_Mutex_Run.Lock();
        while(!m_busy)
        {
            m_Cond_Run.Wait(&m_Mutex_Run, 500);
        }
        m_Mutex_Run.Unlock();


        //这一段相当于V(m_Cond_IsTaskRun)
        m_Mutex_IsTaskRun.Lock();
        m_IsTaskRun = TRUE;
        m_Cond_IsTaskRun.Signal();
        m_Mutex_IsTaskRun.Unlock();

        //在这里执行Run()之后再把线程放回线程池的未用队列
        if(m_Task != NULL)
        {
            m_Task->Run();
            delete m_task;
            //在这一部分还有一段用于确认是否运行完毕的PV操作,暂且省略
            
            //这个函数主要是把当前线程从已用队列挪到未用队列
            ThreadPool::ReclaimThread(this);
        }
        //修改状态
        m_Mutex_IsTaskRun.Lock();
        m_IsTaskRun = FALSE;
        m_Mutex_IsTaskRun.Unlock();
    }

    return NULL;
}
Bool ThreadTask::Active()
{
    m_Mutex_Run.Lock();
    if(!m_busy)
    {
        //这一段相当于V(m_Cond_Run)
        m_busy = TRUE;
        m_Cond_Run.Signal();
        m_Mutex_Run.Unlock();

        //这一段相当于P(m_Cond_IsTaskRun)
        m_Mutex_IsTaskRun.Lock();
        while(!m_IsTaskRun)
        {
            m_Cond_IsTaskRun.Wait(&m_Mutex_IsTaskRun,100);
        }
        m_Mutex_IsTaskRun.Unlock();
        return TRUE;
    }
    m_Mutex_Run.Unlock();
    return TRUE;
}

 

以上是关于线程池的实现设计的主要内容,如果未能解决你的问题,请参考以下文章

看起来是线程池的BUG,但是我认为是源码设计不合理

mysql线程池的实现原理浅析

Linux/设计模式线程池的实现

Linux/设计模式线程池的实现

线程池的设计思想

通用线程池的设计和实现[C语言]