基于EasyDarwin的实现无人机(4G)远程视频传输--(RTSP之TaskThread)

该篇文章参考于http://blog.csdn.net/haolipengzhanshen/article/details/50859062 表示感谢
在上一节中我们知道在EventThread::Entry中调用select_waitevent函数等待连接事件发生,可以是视频采集端推流和客户端拉取流,均会触发这个监听函数。当有事件发生的时候就调用ProcessEvent方法对事件进行相应的处理。

对于建立RTSP连接的请求:
调用TCPListenerSocket::ProcessEvent
此方法调用RTSPListenerSocket的GetSessionTask方法建立一个RTSPSession,并把相应的套接口加入侦听队列,等待RTSP请求。
然后还需调用this->RequestEvent(EV_RE)把建立RTSP连接的请求加入到侦听队列。
在RequestEvent函数中调用select_modwatch(&fEventReq, theMask)将自己感兴趣的事件加入到侦听队列中,

学习TaskThread主要有三个类要关注:
TaskTreadPool: 任务线程池
TaskThread:任务线程
Task: 任务

1. TaskThreadPool
Darwin运行着一个或者多个任务(Task)线程,并将他们统一在线程池TaskThreadPool中管理。任务线程从事件线程中接收RTSP和RTP请求,然后把请求传递到恰当的服务器模块进行处理,把数据包发送给客户端。
缺省情况下,核心服务器为每一个处理器创建一个任务线程。(numProcessors = OS::GetNumProcessors();)
TaskThreadPool::SetNumShortTaskThreads(numShortTaskThreads);
TaskThreadPool::SetNumBlockingTaskThreads(numBlockingThreads);
TaskThreadPool::AddThreads(numThreads);
sServer->InitNumThreads(numThreads);

2. TaskThread
所有的Task对象都将在TaskThread中运行,TaskThread通过运行Task类型对象的Run方法来完成相应Task的处理。TaskThread的个数是可配置的,缺省情况下TaskThread的个数与处理器的个数一致。等待被TaskThread调用并运行的Task放在队列或者堆中。

if (notAService)
    {
        // If we're running off the command-line, don't do the service initiation crap.
        ::StartServer(sXMLParser, &sMessagesSource, sPort, sStatsUpdateInterval, sInitialState,        false,0, kRunServerDebug_Off,sAbsolutePath); // No stats update interval for now
        ::RunServer();
        ::exit(0);
    }

在RunServer.cpp中主函数的这段代码,完成了server的初始化,在StasrtServer中创建了 TaskThread线程池。来看一看这个线程的成员:

class TaskThread : public OSThread
{
    public:

        //Implementation detail: all tasks get run on TaskThreads.

                        TaskThread() :  OSThread(), fTaskThreadPoolElem()
                                        {fTaskThreadPoolElem.SetEnclosingObject(this);}
                        virtual         ~TaskThread() { this->StopAndWaitForThread(); }

    private:

        enum
        {
            kMinWaitTimeInMilSecs = 10  //UInt32
        };

        virtual void    Entry();
        Task*           WaitForTask();

        OSQueueElem     fTaskThreadPoolElem;

        OSHeap              fHeap;
        OSQueue_Blocking    fTaskQueue;


        friend class Task;
        friend class TaskThreadPool;
};

其中的重点是Entry()和WaitForTask()。

Task* TaskThread::WaitForTask()
{
    while (true)
    {
        //获取当前时间
        SInt64 theCurrentTime = OS::Milliseconds();

          /*如果堆中有任务,且任务已经到执行时间,返回该任务。 
        PeekMin函数见OSHeap.h,窃听堆中第一个元素(但不取出)*/  
        if ((fHeap.PeekMin() != NULL) && (fHeap.PeekMin()->GetValue() <= theCurrentTime)) { return (Task*)fHeap.ExtractMin()->GetEnclosingObject();//返回指向Task对象的指针
        }
//如果堆中有任务,但是尚未到执行时间,计算需要等待的时间  
        //if there is an element waiting for a timeout, figure out how long we should wait.  
        SInt64 theTimeout = 0;
        if (fHeap.PeekMin() != NULL)
            theTimeout = fHeap.PeekMin()->GetValue() - theCurrentTime;//计算下超时时间
        Assert(theTimeout >= 0);

        if (theTimeout < 10) theTimeout = 10; //wait... //如果任务队列为空,就等待theTimeout时间后从堆中取出任务返回; //如果任务队列不为空,就不等待,直接取队列中的任务; //从fTaskQueue任务队列中取出OSQueueElem队列元素 OSQueueElem* theElem = fTaskQueue.DeQueueBlocking(this, (SInt32) theTimeout); if (theElem != NULL) { return (Task*)theElem->GetEnclosingObject();//返回Task指针,从堆中取出第一个任务返回  
        }

        if (OSThread::GetCurrent()->IsStopRequested())
            return NULL;
    }   
}

3. Task
每个Task对象有两个主要的方法:Signal和Run。

3.1 Run()
Run()方法是在Task对象获得处理该事件的时间片后运行的,Darwin中的大部分工作都是在不同Task对象的Run()函数中进行的。
比如常见的Task类型是RTSPSession和RTPSession。
在这里程序的返回值是一个重要的信息:
当返回-1时,表明任务已经完全结束,程序会直接删除该Task指针;
当返回0时,线程接着执行下一个Task,表明任务希望在下次传信时被再次立即执行;
当返回一个正数n时,线程会在 n毫秒后再次调用这个Task的Run函数。将该任务加入到Heap中,并且纪录它希望等待的时间。Entry()函数将通过waitfortask()函数进行检测,如果等待的时间到了,就再次运行该任务
(注意; 在我们继承Task类,而重写Run虚函数时,我们第一件要作的事情是运行Task类的GetEvent()函数。)

TaskThread::Entry调用TaskThread::WaitForTask()方法获得下一个需要处理的Task。TaskThread::WaitForTask()首先从TaskThread::fHeap中获得Task,如果TaskThread::fHeap中没有满足条件的Task,则从TaskThread::fTaskQueue中获得Task。

继续分析TaskThread::Entry()函数
TaskThread::Entry调用TaskThread::WaitForTask()方法获得下一个需要处理的Task。
根据theTask->fWriteLock判断是否有读写锁
然后调用theTimeout = theTask->Run(),转到Task::Run函数
if(theTimeout < 0) { //删除task } else if (theTimeout == 0) { //不做处理 } else { // 返回值theTimeout代表了下次处理这个Task需等待的时间 //fHeap.Insert(&theTask->fTimerHeapElem)把Task插入到堆里,并设定等待时间
}

对于EasyDarwin的核心任务线程管理处理,还是比较难懂的,我写文章的时候也是模模糊糊的,多看几遍吧,对于整体框架的应用有好处,我写这篇文章的目的是完成一个事实视频采集的功能。对于线程池的管理,我也是需要学习的。

更多内容请看;http://www.amovauto.com 阿木技术社区,玩也要玩的专业。

你可能感兴趣的:(视频,4g,开源社区,视频传输,EasyDarwin)