Tars源码分析---线程池实现

前言

Tars底层实现了一个线程池库,主要源代码位于tc_thread_poo.(cpp,h)文件中。线程池队列涉及的核心是工作线程和任务队列的设计。本文基于tars中的实现进行介绍。

TC_Thread

线程池的工作线程类(ThreadWorker)继承自TC_Thread,它是tars实现的线程类,实现原理是基于C++的pthread_create函数。外部用户通过调用TC_Thread的start函数来启动线程,新线程的入口为threadEntry函数,该函数会调用TC_Thread的run函数。run函数是具体的线程执行逻辑,它是一个虚函数

virtual void run() = 0;

因此子类可以通过实现这个函数来控制线程的运行逻辑。我们下面我们根据ThreadWorker实现的run函数来看线程池中的线程的执行逻辑。

ThreadWorker

void TC_ThreadPool::ThreadWorker::run()
{
    //调用初始化部分
    TC_FunctorWrapperInterface *pst = _tpool->get();
    if(pst)
    {
        try
        {
            (*pst)();
        }
        catch ( ... )
        {
        }
        delete pst;
        pst = NULL;
    }

    //调用处理部分
    while (!_bTerminate)
    {
        TC_FunctorWrapperInterface *pfw = _tpool->get(this);
        if(pfw != NULL)
        {
            auto_ptr<TC_FunctorWrapperInterface> apfw(pfw);

            try
            {
                (*pfw)();
            }
            catch ( ... )
            {
            }

            _tpool->idle(this);
        }
    }

    //结束
    _tpool->exit();
}

可以看到,run函数的执行流可以简单分为两个部分:
1. 在线程启动时,对线程进行初始化
2. while主循环,不断尝试从任务队列中提取任务并执行。

这里的任务是以仿函数对象的形式存在的,这样就可以使得线程不必关心任务的具体实现逻辑,只需要直接调用仿函数即可。下面我们介绍一下线程池的实现,以及对外提供的接口。

TC_ThreadPool

TC_ThreadPool主要维护以下几个成员数据:

    /**
     * 任务队列
     */
    TC_ThreadQueue<TC_FunctorWrapperInterface*> _jobqueue;

    /**
     * 启动任务
     */
    TC_ThreadQueue<TC_FunctorWrapperInterface*> _startqueue;

    /**
     * 工作线程
     */
    std::vector<ThreadWorker*>                  _jobthread;

    /**
     * 繁忙线程
     */
    std::set<ThreadWorker*>                     _busthread;

    /**
     * 任务队列的锁
     */
    TC_ThreadLock                               _tmutex;

    /**
     * 是否所有任务都执行完毕
     */
    bool                                        _bAllDone;

_jobqueue是任务队列,用户通过调用线程池的exec接口向任务队列中添加任务

void exec(const TC_FunctorWrapper<ParentFunctor> &tf)

_startqueue是中放的是线程启动初始化逻辑。带参start函数负责设置这个队列:

    void start(const TC_FunctorWrapper<ParentFunctor> &tf)

从这个函数的实现可以看到,每个线程的初始化逻辑是一样的。

_jobthread和_busthread是放置线程对象的集合,其中_busthread中存放正在执行任务的线程。

最后,_tmutex是任务锁,_bAllDone是一个状态变量。

TC_ThreadQueue

线程池中的线程在操作TC_ThreadQueue时并没有加锁,这是因为它是一个线程安全的队列。它底层数据存储结构是deque。

TC_ThreadQueue的线程安全性得益于它继承了TC_ThreadLock类:

typedef TC_Monitor<TC_ThreadMutex, TC_ThreadCond> TC_ThreadLock;

该类提供了互斥锁和条件变量来保证多并发操作的安全。通过在TC_ThreadQueue涉及并发竞争问题的操作函数中调用TC_ThreadLock提供的相关操作接口,就可以保证TC_ThreadQueue相应操作函数的线程安全性。

总结

本文以线程池的实现为入口,分别介绍了tars中实现的线程类,线程锁,以及基于线程锁的线程安全对象。所有需要线程安全的对象都可以通过继承TC_ThreadLock基类,然后运用TC_ThreadLock提供的操作函数达到对象的成员数据访问操作的并发安全(当然,这是由于TC_ThreadLock中内置了互斥锁和条件变量)。

猜你喜欢

转载自blog.csdn.net/Swartz2015/article/details/80791979