Scheduler类源代码不完全分析(分量足
来源:互联网 发布:淘宝多功能油炸锅 编辑:程序博客网 时间:2024/05/15 07:43
一,Timer类
1,是Ref的子类,采用引用计数管理内存
2,主要方法update( )实现了一个定时器逻辑
3,抽象类,必须被继承使用
virtualvoid trigger() = 0;//到时触发的方法
virtualvoid cancel() = 0; //取消定时器
//两个为纯虚函数,需要在子类重载实现
protected:
Scheduler* _scheduler; // weak ref 弱引用,在这个指针被赋值时并没有增加对_scheduler的引用计数
float _elapsed; //度过的时间
bool _runForever; //标记是否永久运行
bool _useDelay; //标记是否使用延迟
unsigned int _timesExecuted; //纪录已经执行的次数
unsigned int _repeat; //0 = once, 1 is 2 executed 定义执行的总次数(_repeat+1次)
float _delay; //延迟时间
float _interval; //时间间隔,隔_interval调用一次
//初始化方法
void setupTimerWithInterval(floatseconds, unsigned intrepeat, float delay);
{
_elapsed = -1;
_interval = seconds;
_delay = delay;
_useDelay = (_delay > 0.0f) ?true : false;
_repeat = repeat;
_elapsed = -1;
_interval = seconds;
_delay = delay;
_useDelay = (_delay > 0.0f) ?true : false;
_repeat = repeat;
_runForever= (_repeat== CC_REPEAT_FOREVER) ? true : false;
//无限重复次数为一个unsignedint的最大值 #define CC_REPEAT_FOREVER (UINT_MAX -1)
}
4,Timer的子类TimerTargetSelector(绑定Ref基类对象)
//初始化方法
bool initWithSelector(Scheduler* scheduler,SEL_SCHEDULE selector, Ref* target,float seconds, unsignedint repeat, floatdelay);
//SEL_SCHEDULE是一个Ref函数指针定义
voidTimerTargetSelector::trigger()
{
if (_target && _selector)
{
(_target->*_selector)(_elapsed);//调用初始化传进来的回调方法
}
}
void TimerTargetSelector::cancel()
{
_scheduler->unschedule(_selector,_target);
{
if (_target && _selector)
{
(_target->*_selector)(_elapsed);//调用初始化传进来的回调方法
}
}
void TimerTargetSelector::cancel()
{
_scheduler->unschedule(_selector,_target);
}
5,TimerTargetCallback(绑定任意对象)
boolTimerTargetCallback::initWithCallback(Scheduler* scheduler,const ccSchedulerFunc& callback, void *target, const std::string& key,float seconds, unsignedint repeat, floatdelay)
{
_scheduler= scheduler;
_target = target;
_callback= callback;
_key = key;
setupTimerWithInterval(seconds, repeat, delay);
return true;
}
void TimerTargetCallback::trigger()
{
if (_callback)
{
_callback(_elapsed);
}
}
void TimerTargetCallback::cancel()
{
_scheduler->unschedule(_key,_target);
{
_scheduler= scheduler;
_target = target;
_callback= callback;
_key = key;
setupTimerWithInterval(seconds, repeat, delay);
return true;
}
void TimerTargetCallback::trigger()
{
if (_callback)
{
_callback(_elapsed);
}
}
void TimerTargetCallback::cancel()
{
_scheduler->unschedule(_key,_target);
}
6.TimerScriptHandler与以上两个类似,有关脚本的
二,Scheduler类
巨多。。好难。。看不懂。。
首先,
Scheduler有两种调度模式:
1,按帧调度(被集成在Node类中)
数据结构:
typedefstruct _listEntry //双向链表
{
struct _listEntry *prev, *next;
ccSchedulerFunc callback;
void *target;
int priority;
bool paused;
bool markedForDeletion;// 标识是否需要删除selector will no longer be called and entry will be removed at end of the next tick
} tListEntry;
{
struct _listEntry *prev, *next;
ccSchedulerFunc callback;
void *target;
int priority;
bool paused;
bool markedForDeletion;// 标识是否需要删除selector will no longer be called and entry will be removed at end of the next tick
} tListEntry;
typedefstruct _hashUpdateEntry //
{
tListEntry **list; // Which list does it belong to ?
tListEntry *entry; // entry in the list
void *target;
ccSchedulerFunc callback;
UT_hash_handle hh;
tListEntry **list; // Which list does it belong to ?
tListEntry *entry; // entry in the list
void *target;
ccSchedulerFunc callback;
UT_hash_handle hh;
} tHashUpdateEntry;
提供了3个双向链表来维护(按优先级区分,Scheduler::update调用时,遍历三个链表来区分优先级)
struct_listEntry *_updatesNegList; // list of priority < 0
struct _listEntry *_updates0List; // list priority == 0
struct _listEntry *_updates0List; // list priority == 0
struct _listEntry *_updatesPosList; // list priority > 0
提供了一个_hashForUpdates(_hashUpdateEntry结构体链表头指针)来实现快速查找等操作
相关构造函数:
void schedulePerFrame(constccSchedulerFunc& callback,void *target, intpriority, bool paused);
// update specific
void priorityIn(struct_listEntry **list, const ccSchedulerFunc& callback,void *target, int priority, bool paused);//优先级不为0的添加方法
void appendIn(struct_listEntry **list, const ccSchedulerFunc& callback,void *target, bool paused);//优先级为0的添加方法
2,按时间间隔调度
数据结构:
typedef struct _hashSelectorEntry
{
ccArray *timers;
void *target;
int timerIndex;
Timer *currentTimer;
bool currentTimerSalvaged;//清理标识
bool paused;
UT_hash_handle hh;
{
ccArray *timers;
void *target;
int timerIndex;
Timer *currentTimer;
bool currentTimerSalvaged;//清理标识
bool paused;
UT_hash_handle hh;
} tHashTimerEntry;
由一个HashTable来维护(开源第三方数据结构Uthash),所有的时间间隔调度事件都存储在这个HashTable中,这个HashTable的 Key = target(注册的对象),Value = tHashTimerEntry结构体,_hashForTimers是_hashSelectorEntry这个结构体链表的头指针
按时间间隔调度还可以分为两种对象形式,一是Ref基类的对象,一种是任意对象
结构体中:
tHashTimerEntry这个结构体用来记录一个Ref对象所有注册的定时器
Timer类是用来绑定对象与回调方法(不同对象有不同的绑定方法)
timers是一个数组,用来存储所有的Timer对象,所以一个对象可以注册多个调度事件。
其次,主要逻辑
程序每一次事件循环mainLoop()中都会调用Scheduler::update()(此时的scheduler是全局共享的一个单例对象),然后在这个update中会 1,遍历三个双向链表,查找当前可以执行的回调。2,遍历hashtable中的对象(结构体),调用每个结构体中timers数组中所有Timer对象的update()方法(此时的update是Timer类中的,实现定时器功能)。
再,贴源代码解释
ps:
HASH_FIND_PTR是uthash中的接口,作用是查找hash表中是否存在某个数据
如HASH_FIND_PTR(_hashForUpdates, &target, hashElement);
查找_hashForUpdates表中key为target的数据,如果存在则返回给hashElement,没有则无操作
HASH_ADD_PTR(_hashForTimers,target, element);//添加key = target,value = element的数据进表中
HASH_DEL(_hashForUpdates, element);//删除数据
1.按帧调度
首先在Node::scheduleUpdate()中会最终调用到Scheduler::schedulePerFrame(注册按帧更新的方法)
voidScheduler::schedulePerFrame(constccSchedulerFunc& callback,void *target, intpriority, bool paused)
{
//在_hashForUpdates中检查当前target是否有注册
tHashUpdateEntry *hashElement = nullptr;
HASH_FIND_PTR(_hashForUpdates, &target, hashElement);
if (hashElement)//存在数据,不进行插入,也就是说一个node只能加入一次帧调度列表中,也只能有一个回调过程,即Node::update(),所以如果想实现自己的方法应该重载这个update。
{
// check if priority has changed 判断优先级是否变化
if ((*hashElement->list)->priority!= priority)//优先级改变
// check if priority has changed 判断优先级是否变化
if ((*hashElement->list)->priority!= priority)//优先级改变
{
if (_updateHashLocked)//正在执行update,则暂时不删除
{
CCLOG("warning: you CANNOT change update priority in scheduled function");
hashElement->entry->markedForDeletion= false;
hashElement->entry->paused= paused;
return;
}
else
CCLOG("warning: you CANNOT change update priority in scheduled function");
hashElement->entry->markedForDeletion= false;
hashElement->entry->paused= paused;
return;
}
else
{
//不在执行update,则直接删除
// will be added again outside if (hashElement).
unscheduleUpdate(target);
}
}
else
unscheduleUpdate(target);
}
}
else
{
//优先级没变,标记不被删除,退出
hashElement->entry->markedForDeletion= false;
hashElement->entry->paused= paused;
return;
}
}
hashElement->entry->paused= paused;
return;
}
}
//不存在数据,则按照优先级插入到对应的双向链表中
// most of the updates are going to be 0, that's way there
// is an special list for updates with priority 0
if (priority == 0)
{
appendIn(&_updates0List, callback, target, paused);
}
else if (priority < 0)
{
priorityIn(&_updatesNegList, callback, target, priority, paused);
}
else
{
// priority > 0
priorityIn(&_updatesPosList, callback, target, priority, paused);
}
// is an special list for updates with priority 0
if (priority == 0)
{
appendIn(&_updates0List, callback, target, paused);
}
else if (priority < 0)
{
priorityIn(&_updatesNegList, callback, target, priority, paused);
}
else
{
// priority > 0
priorityIn(&_updatesPosList, callback, target, priority, paused);
}
}
voidScheduler::appendIn(_listEntry**list, const ccSchedulerFunc& callback,void *target, boolpaused)
{
tListEntry*listElement = new tListEntry();//建双向链表
//赋值
listElement->callback= callback;
listElement->target= target;
listElement->paused= paused;
listElement->priority= 0;
listElement->markedForDeletion= false;
//添加到*list链表中
DL_APPEND(*list, listElement);
{
tListEntry*listElement = new tListEntry();//建双向链表
//赋值
listElement->callback= callback;
listElement->target= target;
listElement->paused= paused;
listElement->priority= 0;
listElement->markedForDeletion= false;
//添加到*list链表中
DL_APPEND(*list, listElement);
// update hash entry for quicker access再将这个新的链表添加到_hashForUpdates中
tHashUpdateEntry *hashElement = (tHashUpdateEntry*)calloc(sizeof(*hashElement),1);
hashElement->target= target;
hashElement->list= list;
hashElement->entry= listElement;
HASH_ADD_PTR(_hashForUpdates,target, hashElement);
hashElement->target= target;
hashElement->list= list;
hashElement->entry= listElement;
HASH_ADD_PTR(_hashForUpdates,target, hashElement);
}
priorityIn是按照优先级加入表中
取消注册unschedulerupdate()
2.按时间间隔调度
注册方法有两种
任意参数:
voidschedule(constccSchedulerFunc& callback,void *target, floatinterval, unsigned intrepeat, float delay, bool paused, const std::string& key);
void schedule(constccSchedulerFunc& callback,void *target, floatinterval, bool paused, const std::string& key);
Ref基类:
void schedule(SEL_SCHEDULEselector, Ref *target, float interval, unsigned intrepeat, float delay, bool paused);
void schedule(SEL_SCHEDULEselector, Ref *target, float interval, bool paused);
voidScheduler::schedule(SEL_SCHEDULEselector, Ref *target, float interval, unsigned intrepeat, float delay, bool paused)
{
CCASSERT(target,"Argument target must be non-nullptr");
tHashTimerEntry*element = nullptr;
HASH_FIND_PTR(_hashForTimers, &target, element);//查找当前target有没有注册
if (! element)
{
CCASSERT(target,"Argument target must be non-nullptr");
tHashTimerEntry*element = nullptr;
HASH_FIND_PTR(_hashForTimers, &target, element);//查找当前target有没有注册
if (! element)
{
//没有的话分配空间,设置target后加入到_hashForTimers中,并设置状态为pause
element = (tHashTimerEntry*)calloc(sizeof(*element),1);
element->target= target;
HASH_ADD_PTR(_hashForTimers,target, element);
// Is this the 1st element ? Then set the pause level to all the selectors of this target
element->paused= paused;
}
else
element->target= target;
HASH_ADD_PTR(_hashForTimers,target, element);
// Is this the 1st element ? Then set the pause level to all the selectors of this target
element->paused= paused;
}
else
{
CCASSERT(element->paused == paused,"”);//设置状态为pause
}
if (element->timers== nullptr)
if (element->timers== nullptr)
{
//如果timers数组为空,则新分配10个空间
element->timers= ccArrayNew(10);
}
else
}
else
{
//不为空,则遍历timers数组
for (int i = 0; i < element->timers->num; ++i)
{
TimerTargetSelector*timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]);
//查找跟当前selector相同的timer对象,更新他的时间间隔,也就是说一个selector只能注册一次
if(timer && selector == timer->getSelector())
{
CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
timer->setInterval(interval);
return;
}
{
TimerTargetSelector*timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]);
//查找跟当前selector相同的timer对象,更新他的时间间隔,也就是说一个selector只能注册一次
if(timer && selector == timer->getSelector())
{
CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
timer->setInterval(interval);
return;
}
}
ccArrayEnsureExtraCapacity(element->timers,1);//将timers数组空间扩大1个单位,确保能存储
}
//新建timer对象,并封装selector,target等参数后添加到timers数组中
TimerTargetSelector*timer = new (std::nothrow)TimerTargetSelector();
timer->initWithSelector(this, selector, target, interval, repeat, delay);
ccArrayAppendObject(element->timers, timer);
timer->release();//因为添加到数组的操作有retain(),所以应该release一次
timer->initWithSelector(this, selector, target, interval, repeat, delay);
ccArrayAppendObject(element->timers, timer);
timer->release();//因为添加到数组的操作有retain(),所以应该release一次
}
voidScheduler::schedule(SEL_SCHEDULEselector, Ref *target, float interval, bool paused)
{
this->schedule(selector, target, interval,CC_REPEAT_FOREVER,0.0f, paused);
{
this->schedule(selector, target, interval,CC_REPEAT_FOREVER,0.0f, paused);
}
voidScheduler::schedule(constccSchedulerFunc& callback,void *target, floatinterval, unsigned intrepeat, float delay, bool paused, const std::string& key)
{
if (timer && key == timer->getKey())
{
CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
timer->setInterval(interval);
return;
{
CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
timer->setInterval(interval);
return;
}
}
voidScheduler::schedule(constccSchedulerFunc& callback,void *target, floatinterval, bool paused, const std::string& key)
{
this->schedule(callback, target, interval,CC_REPEAT_FOREVER,0.0f, paused, key);
{
this->schedule(callback, target, interval,CC_REPEAT_FOREVER,0.0f, paused, key);
}
Tips:任意参数的注册方法和定义中都比Ref类的多了一个Key,这是因为SEL_SCHEDULE可以当成key,ccSchedulerFunc不能,因为前者有唯一的标识(查看指向类中方法的函数指针)
取消注册的方法也很简单
3.Scheduler::update()
// main loop
void Scheduler::update(floatdt)
{
_updateHashLocked= true;//状态锁
if (_timeScale != 1.0f)//时间线
{
dt *= _timeScale;
}
//
void Scheduler::update(floatdt)
{
_updateHashLocked= true;//状态锁
if (_timeScale != 1.0f)//时间线
{
dt *= _timeScale;
}
//
// Selector callbacks
//
//处理按帧调度
// Iterate over all the Updates’ selectors
tListEntry *entry, *tmp;//定义两个指针用于链表遍历
//依次遍历三个双向链表,对活动有效的定时器进行回调
// updates with priority < 0
DL_FOREACH_SAFE(_updatesNegList, entry, tmp)
{
if ((! entry->paused) && (! entry->markedForDeletion))
{
entry->callback(dt);
}
}
// updates with priority == 0
DL_FOREACH_SAFE(_updates0List, entry, tmp)
{
if ((! entry->paused) && (! entry->markedForDeletion))
{
entry->callback(dt);
}
}
// updates with priority > 0
DL_FOREACH_SAFE(_updatesPosList, entry, tmp)
{
if ((! entry->paused) && (! entry->markedForDeletion))
{
entry->callback(dt);
}
DL_FOREACH_SAFE(_updatesNegList, entry, tmp)
{
if ((! entry->paused) && (! entry->markedForDeletion))
{
entry->callback(dt);
}
}
// updates with priority == 0
DL_FOREACH_SAFE(_updates0List, entry, tmp)
{
if ((! entry->paused) && (! entry->markedForDeletion))
{
entry->callback(dt);
}
}
// updates with priority > 0
DL_FOREACH_SAFE(_updatesPosList, entry, tmp)
{
if ((! entry->paused) && (! entry->markedForDeletion))
{
entry->callback(dt);
}
}
//处理按时间间隔调度
// Iterate over all the custom selectors
// Iterate over all the custom selectors
for (tHashTimerEntry *elt =_hashForTimers; elt != nullptr; )
//遍历_hashForTimers中的对象
{
_currentTarget = elt; //主循环中用来标记当前执行到哪个target对象
_currentTargetSalvaged= false;//标记_currentTarget是否需要进行清除
if (! _currentTarget->paused)
{
// The 'timers' array may change while inside this loop
//遍历timers数组
for (elt->timerIndex= 0; elt->timerIndex< elt->timers->num; ++(elt->timerIndex))
{
elt->currentTimer= (Timer*)(elt->timers->arr[elt->timerIndex]);
elt->currentTimerSalvaged= false;
elt->currentTimer->update(dt);//调用Timer::update()
if(elt->currentTimerSalvaged)
{
// The currentTimer told the remove itself. To prevent the timer from
// accidentally deallocating itself before finishing its step, we retained
{
elt->currentTimer= (Timer*)(elt->timers->arr[elt->timerIndex]);
elt->currentTimerSalvaged= false;
elt->currentTimer->update(dt);//调用Timer::update()
if(elt->currentTimerSalvaged)
{
// The currentTimer told the remove itself. To prevent the timer from
// accidentally deallocating itself before finishing its step, we retained
// it. Now that step is done, it's safe to release it.
// currentTimerSalvaged的作用是标记当前这个定时器是否已经失效,在设置失效的时候我们对定时器增加过一次引用记数,这里调用release来减少那次引用记数,这样释放很安全,这里用到了这个小技巧,延迟释放,这样后面的程序不会出现非法引用定时器指针而出现错误
elt->currentTimer->release();
}
elt->currentTimer= nullptr;
}
}
// elt, at this moment, is still valid
}
elt->currentTimer= nullptr;
}
}
// elt, at this moment, is still valid
// so it is safe to ask this here (issue #490)
//下面可能会清理当前对象,所以趁还存活时找到链表的下一指针
elt = (tHashTimerEntry*)elt->hh.next;
// only delete currentTarget if no actions were scheduled during the cycle (issue #481)
if (_currentTargetSalvaged&& _currentTarget->timers->num== 0)
{
removeHashElement(_currentTarget);
}
}
// only delete currentTarget if no actions were scheduled during the cycle (issue #481)
if (_currentTargetSalvaged&& _currentTarget->timers->num== 0)
{
removeHashElement(_currentTarget);
}
}
// delete all updates that are marked for deletion下面三个也是清理工作
// updates with priority < 0
DL_FOREACH_SAFE(_updatesNegList, entry, tmp)
{
if (entry->markedForDeletion)
{
this->removeUpdateFromHash(entry);
}
}
// updates with priority == 0
DL_FOREACH_SAFE(_updates0List, entry, tmp)
{
if (entry->markedForDeletion)
{
this->removeUpdateFromHash(entry);
}
}
// updates with priority > 0
DL_FOREACH_SAFE(_updatesPosList, entry, tmp)
{
if (entry->markedForDeletion)
{
this->removeUpdateFromHash(entry);
}
}
_updateHashLocked= false;
_currentTarget= nullptr;
#if CC_ENABLE_SCRIPT_BINDING
//
// Script callbacks
//
// Iterate over all the script callbacks
if (!_scriptHandlerEntries.empty())
{
for (auto i = _scriptHandlerEntries.size() -1; i >=0; i--)
{
SchedulerScriptHandlerEntry* eachEntry =_scriptHandlerEntries.at(i);
if(eachEntry->isMarkedForDeletion())
{
_scriptHandlerEntries.erase(i);
}
elseif (!eachEntry->isPaused())
{
eachEntry->getTimer()->update(dt);
}
}
}
#endif
//
// Functions allocated from another thread
//
//多线程处理函数的定时任务
// Testing size is faster than locking / unlocking.
// And almost never there will be functions scheduled to be called.
if( !_functionsToPerform.empty() ) {
_performMutex.lock();
// fixed #4123: Save the callback functions, they must be invoked after '_performMutex.unlock()', otherwise if new functions are added in callback, it will cause thread deadlock.
autotemp = _functionsToPerform;
_functionsToPerform.clear();
_performMutex.unlock();
for(const auto &function : temp ) {
function();
}
}
DL_FOREACH_SAFE(_updatesNegList, entry, tmp)
{
if (entry->markedForDeletion)
{
this->removeUpdateFromHash(entry);
}
}
// updates with priority == 0
DL_FOREACH_SAFE(_updates0List, entry, tmp)
{
if (entry->markedForDeletion)
{
this->removeUpdateFromHash(entry);
}
}
// updates with priority > 0
DL_FOREACH_SAFE(_updatesPosList, entry, tmp)
{
if (entry->markedForDeletion)
{
this->removeUpdateFromHash(entry);
}
}
_updateHashLocked= false;
_currentTarget= nullptr;
#if CC_ENABLE_SCRIPT_BINDING
//
// Script callbacks
//
// Iterate over all the script callbacks
if (!_scriptHandlerEntries.empty())
{
for (auto i = _scriptHandlerEntries.size() -1; i >=0; i--)
{
SchedulerScriptHandlerEntry* eachEntry =_scriptHandlerEntries.at(i);
if(eachEntry->isMarkedForDeletion())
{
_scriptHandlerEntries.erase(i);
}
elseif (!eachEntry->isPaused())
{
eachEntry->getTimer()->update(dt);
}
}
}
#endif
//
// Functions allocated from another thread
//
//多线程处理函数的定时任务
// Testing size is faster than locking / unlocking.
// And almost never there will be functions scheduled to be called.
if( !_functionsToPerform.empty() ) {
_performMutex.lock();
// fixed #4123: Save the callback functions, they must be invoked after '_performMutex.unlock()', otherwise if new functions are added in callback, it will cause thread deadlock.
autotemp = _functionsToPerform;
_functionsToPerform.clear();
_performMutex.unlock();
for(const auto &function : temp ) {
function();
}
}
}
0 0
- Scheduler类源代码不完全分析(分量足
- Node类源代码不完全分析
- Director类源代码不完全分析
- 调度器Scheduler类源码分析
- kubernetes scheduler 模块分析
- Kubernetes Scheduler源码分析
- kubernetes之Scheduler分析
- 源码分析参考:Scheduler
- Storm-源码分析- Scheduler (backtype.storm.scheduler)
- IT产业不完全分析
- {smartassembly}不完全分析
- 【Cocos2d-x 3.x】 调度器Scheduler类源码分析
- SerialPort类源代码分析
- SerialPort类源代码分析
- SerialPort类源代码分析
- SerialPort类源代码分析
- SerialPort类源代码分析
- CSerialPort类源代码分析
- css3动画图片变形,自定义字体
- Director类源代码不完全分析
- 如何判断一个字符串中的括号是否匹配
- VS2010 Qt5开发实用技能
- 对称加密和非对称加密
- Scheduler类源代码不完全分析(分量足
- Java中获取资源问题
- (奶油小刀插件) android-butterknife-zelezny 插件使用
- CentOS 6 使用 yum 安装MongoDB及服务器端配置
- Android 网络编程
- 关于规划的寓言故事:把一张纸折叠51次 ------意义不在折纸,而在规划,重在规划!!!
- C语言中结构体与指针的若干问题(在数据结构中的应用)
- hdu 2254(矩阵)
- MAC 如何在MAC系统中保存txt格式文本