出于资源加载效率方面的考虑,我们使用无锁多线程来实现这一模块。
类型设计:
1. 任务信息:
struct TaskInfo
{
T task; //任务标识
TaskType taskType; //任务类型, LOAD/FREE/NONE,(主线程可写,子线程只读)
TaskState taskState; //任务当前状态, NA/TODO/DONE,(主线程可写,子线程只读)
unsigned int uiPriority; //任务优先级,(主线程可写,子线程只读)
unsigned int uiPushbackTimes; //任务被搁置的次数,(主线程可写,子线程只读)
void* handle; //资源地址,(主线程只读,子线程可写)
unsigned long ulSize; //资源长度,(主线程只读,子线程可写)
};
2. 任务队列:
class TaskQueue
{
//仅供主线程访问
//在TaskQueue中,为所资源建立索引
void Initialize();
//仅供主线程访问
//TaskQueue中任务数不变,仅仅将资源对应的任务类型改为LOAD/FREE,任务状态//改为TODO
void AddTask();
//仅供主线程访问
//仅将资源对应的任务类型改为NONE
void RemoveTask();
//仅供子线程访问
//从任务队列中找到最需要执行的一个任务,由任务调度算法决定
TaskInfo GetTask();
//仅供子线程访问
//任务完成后,将资源地址传给任务的handle,大小传给任务的ulSize
void TaskDone();
};
加载流程:
1. 初始化任务队列TaskQueue,为每个资源定义一个TaskInfo 对象加入队列,置初始状态。
2. 当有资源需要加载/释放时,主线程调用AddTask()改变该资源对应的TaskInfo的状态
3. 子线程循环调用GetTask(),从TaskQueue中取得要执行的任务,若有,则执行之。
4. 子线程执行完任务,调用TaskDone()将资源地址和长度保存;若为释放任务,则将资源地址置NULL,长度置0.
说明:
1. 任务队列长度固定,所以避免了插入/删除造成的主/子线程信息的不一致。
2. 对于同一变量,主线程和子线程只有一方具有可写权,从而避免信息冲突。
3. 主线程和子线程,一方读,另一方写,这种情况下,信息的不一致可通过程序逻辑加以避免。
本文代码片段均为伪代码,不可直接使用。
由于作者水平有限,不足之处,还望大家批评指正!