我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!
创意总是一点点积累的。
我曾有幸参加了LCUI开源项目中的一小部分贡献,这里记录一下解决其中一个关于定时器问题。在解决问题的当中,我认为串联起了曾经看过的Linux的内链表的概念是我解决该工具的需求的关键。说明创意源于积累,只有看过足够多的代码,你才会有能力解决、有创意迸发出来。 定时器用于将一些操作推迟到指定时间之后执行。
问题背景
一开始的LCUI 的定时器都是在主线程中处理的,这意味着定时器的时间粒度受到帧率的限制,不能小于每帧的停留时间。 因此,我们必须重新设计结构:原有的定时器结构依赖过多,重新设计的结构要求:
- 尽量保留原有结构,去掉过多的依赖项。
- 重新设计的数据结构使用**有序链表**。
- 具有**有序性**,可以**快速**找到**到期**任务;
- 其**过期执行**、**插入**(添加定时任务)和**删除**(取消定时任务)的频率比较高。
-
执行到期任务:**不依赖事件循环,**列如,用一个列表记录定时器,每当处理函数被调用时从列表取出已超时的定时器,然后调用这些定时器的回调函数。
-
不再是全局共用同一个定时器列表。
创意发散
在思考如何实现的时候,我想了我曾经调研过的Linux的链表结构!
Linux 中的链表采用了数据与结点分离的实现方式。这样在进行遍历等操作的时候,就可以大大的提升效率。
struct list_head {
struct list_head *next, *prev;
};
在这个结构体类型定义中,只包含两个指向list_head结构的的指针prev和next,并没有数据成员,其主要作用就是嵌套在其它结构体类型的定义中起到链接作用。
而在需要链表组织的数据,只需要包含一个struct list_head成员就可以把数据链接起来。这种通用的链表结构避免了为每个数据项类型定义自己的链表的操作。
实现结果
终于,通过借助Linux内常用的数据结构的内链表的概念和导师的指导,我将原实现中的互斥锁等元素一处,成功实现了定时器的开发,下面以去年当时 LCUI 中更新的主线程定时器部分代码作为使用展示。
/*----------------------------- Timer --------------------------------*/
static timer_list_t *main_timer_list = NULL;
static LCUI_Mutex mutex; /**< 定时器记录操作互斥锁 */
/*----------------------------- Private ------------------------------*/
//处理定时器链表
size_t lcui_timer_list_process()
{
size_t count = 0;
LCUIMutex_Lock(&mutex);
count = timer_list_process(main_timer_list);
LCUIMutex_Unlock(&mutex);
return count;
}
//初始化定时器列表
void lcui_timer_list_create(void)
{
LCUIMutex_Init(&mutex);
main_timer_list = timer_list_create();
}
//销毁定时器列表
void lcui_timer_list_destroy()
{
if (!main_timer_list) {
return;
}
timer_list_destroy(main_timer_list);
LCUIMutex_Destroy(&mutex);
main_timer_list = NULL;
}
//销毁定时器
int lcui_timer_destroy(int timer_id)
{
int ret;
//加锁
LCUIMutex_Lock(&mutex);
ret = timer_destroy(main_timer_list, timer_id);
LCUIMutex_Unlock(&mutex);
return ret;
}
//暂定操作
int lcui_timer_pause(int timer_id)
{
int ret;
LCUIMutex_Lock(&mutex);
ret = timer_pause(main_timer_list, timer_id);
LCUIMutex_Unlock(&mutex);
return ret;
}
//继续被暂定的定时器
int lcui_timer_continue(int timer_id)
{
int ret;
LCUIMutex_Lock(&mutex);
ret = timer_continue(main_timer_list, timer_id);
LCUIMutex_Unlock(&mutex);
return ret;
}