cocos2dx 多线程 一个典型的生产者与消费者模型 HttpClient

HttpClient 是extensions/network的工具类 提供http请求支持 使用多线程调用crul实现 此类是在基于cocos2dx 使用多线程技术的很好的范例

设计上比较完整的诠释了 生产者与消费者模型

  1. * 生产者与消费者模型中,要保证以下几点: 
  2. * 1 同一时间内只能有一个生产者生产     生产方法加锁
  3. * 2 同一时间内只能有一个消费者消费     消费方法加锁
  4. * 3 生产者生产的同时消费者不能消费     生产方法加锁
  5. * 4 消费者消费的同时生产者不能生产     消费方法加锁
  6. * 5 共享空间空时消费者不能继续消费     消费前循环判断是否为空,空的话将该线程wait,释放锁允许其他同步方法执行 
  7.   * 6 共享空间满时生产者不能继续生产     生产前循环判断是否为满,满的话将该线程wait,释放锁允许其他同步方法执行  

 


 

 

1.生产方法

此方法在cocos主线程中调用主要将request对象放到s_requestQueue队列中

HttpClient的工作线程方法中会对s_requestQueue中的request对象进行消费

互斥锁s_requestQueueMutex在此处保证 1 同一时间内只能有一个生产者生产 3 生产者生产的同时消费者不能消费

 1 //Add a get task to queue
 2 void CCHttpClient::send(CCHttpRequest* request)
 3 {    
 4     if (false == lazyInitThreadSemphore()) 
 5     {
 6         return;
 7     }
 8     
 9     if (!request)
10     {
11         return;
12     }
13         
14     ++s_asyncRequestCount;
15     
16     request->retain();
17     //同步队列进行生产    
18     pthread_mutex_lock(&s_requestQueueMutex);
19     s_requestQueue->addObject(request);
20     pthread_mutex_unlock(&s_requestQueueMutex);
21     
22     // Notify thread start to work
23     pthread_cond_signal(&s_SleepCondition);
24 }

2.消费方法

工作线程方法networkThread()中对s_requestQueue中的request对象进行消费

互斥锁s_requestQueueMutex在此处保证 2 同一时间内只能有一个消费者消费 4 消费者消费的同时生产者不能生产

1         //同步队列进行消费
      pthread_mutex_lock(&s_requestQueueMutex); //Get request task from queue 2 if (0 != s_requestQueue->count()) 3 { 4 request = dynamic_cast<CCHttpRequest*>(s_requestQueue->objectAtIndex(0)); 5 s_requestQueue->removeObjectAtIndex(0); 6 // request's refcount = 1 here 7 } 8 pthread_mutex_unlock(&s_requestQueueMutex);

3.共享空间限制

在工作线程中 当 s_requestQueue->count()为0 时request=NULL此时没有可消费的原料线程Wait 直到

主线程再次调用生产方法void CCHttpClient::send(CCHttpRequest* request) 通过 pthread_cond_signal(&s_SleepCondition);发出信号唤醒工作线程

 

pthread_cond_wait保证 5 共享空间空时消费者不能继续消费     消费前循环判断是否为空,空的话将该线程wait,释放锁允许其他同步方法执行

1        if (NULL == request)
2         {
3             // Wait for http request tasks from main thread
4             pthread_cond_wait(&s_SleepCondition, &s_SleepMutex);
5             continue;
6         }

对于 6 共享空间满时生产者不能继续生产 生产前循环判断是否为满,满的话将该线程wait,释放锁允许其他同步方法执行
由于 s_requestQueue 上线控制意义不大 而且如果要实现此规范就意味着生产线程不能再是主线程


 

基于上述模型还不够 我们还需要在子线程完成工作后把数据交给主线程

1.轮询方法注册

在 cocos2dx 中可以基于定时轮询机制,HttpClient在构造器中进行了注册,轮询时调用dispatchResponseCallbacks()方法

1 CCDirector::sharedDirector()->getScheduler()->scheduleSelector(
2                     schedule_selector(CCHttpClient::dispatchResponseCallbacks), this, 0, false);
3     CCDirector::sharedDirector()->getScheduler()->pauseTarget(this);
4 }

2.轮询方法实现

HttpClient工作线程进行网络请求(调curl),数据返回后将response对象  s_responseQueue->addObject(response);  到此处工作线程已经完成任务

现在取数据的任务就交给主线程轮询时调用的dispatchResponseCallbacks()方法

oid CCHttpClient::dispatchResponseCallbacks(float delta)
{
    // CCLog("CCHttpClient::dispatchResponseCallbacks is running");
    
    CCHttpResponse* response = NULL;
    //同步队列进行消费
    pthread_mutex_lock(&s_responseQueueMutex);
    if (s_responseQueue->count())
    {
        response = dynamic_cast<CCHttpResponse*>(s_responseQueue->objectAtIndex(0));
        s_responseQueue->removeObjectAtIndex(0);
    }
    pthread_mutex_unlock(&s_responseQueueMutex);
    
    if (response)
    {
        --s_asyncRequestCount;
        
        CCHttpRequest *request = response->getHttpRequest();
        CCObject *pTarget = request->getTarget();
        SEL_HttpResponse pSelector = request->getSelector();

        if (pTarget && pSelector) 
        {
        //回调方法指针 (pTarget
->*pSelector)(this, response); } response->release(); } if (0 == s_asyncRequestCount) { CCDirector::sharedDirector()->getScheduler()->pauseTarget(this); } }

到此为止可以轻完成多线程任务了

posted @ 2015-06-04 15:17  thc  阅读(756)  评论(0)    收藏  举报