cocos2dx 多线程 一个典型的生产者与消费者模型 HttpClient
HttpClient 是extensions/network的工具类 提供http请求支持 使用多线程调用crul实现 此类是在基于cocos2dx 使用多线程技术的很好的范例
设计上比较完整的诠释了 生产者与消费者模型
- * 生产者与消费者模型中,要保证以下几点:
- * 1 同一时间内只能有一个生产者生产 生产方法加锁
- * 2 同一时间内只能有一个消费者消费 消费方法加锁
- * 3 生产者生产的同时消费者不能消费 生产方法加锁
- * 4 消费者消费的同时生产者不能生产 消费方法加锁
- * 5 共享空间空时消费者不能继续消费 消费前循环判断是否为空,空的话将该线程wait,释放锁允许其他同步方法执行
- * 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); } }
到此为止可以轻完成多线程任务了
浙公网安备 33010602011771号