- TASK #0 gradescope
- TASK #1 - LRU REPLACEMENT POLICY
- TASK #2 - BUFFER POOL MANAGER INSTANCE
- TASK #3 - PARALLEL BUFFER POOL MANAGER
TASK #0 gradescope
0.0 关于线上测试的天坑
在gradescope上测试代码时,没有通过的案例如果显示的是超时,那不一定是因为死锁或者死循环!
- 我没有写线程安全的代码,显示的是超市
- 我代码有的部分会造成内存泄漏,显示的还是超时
- 死锁或者replacer复杂度太高确实有可能造成超时
1.1线程安全
要求了线程安全但又没有说具体怎么做,以至于在最后才回来考虑上锁的问题。
其实很简单,在头文件内声明一个锁。在实现的每个函数开头上锁,结尾释放就行。
// 头文件 #include// 声明 std::mutex lock_; //上锁(请求锁),释放锁 lock_.lock(); lock_.unlock();
注意事项:
1.每个return之前都要记得释放锁
2.关于死锁,如果都是开头上锁结尾解锁,根本不会有产生死锁的条件(请求并保持)。
3.如果在一个函数里调用了另一个使用lock的函数,百分百会卡死,因为这个函数的lock还未释放,调用的函数就会一直等待lock。解决办法:在函数调用前释放锁,调用后申请锁。(但这个project可其实以避免这种调用关系)
1.2 upin 和 pin 如何理解
pin():就理解成有人正在使用这个页面,不能用来作为牺牲了,故需要删除当前frame_id(而不是放在末尾)
unpin():lru_relpacer不需要考虑什么时候需要unpin,直接理解成当前frame_id可以作为牺牲id,装入队列就行。
1.3 优化搜索效率
使用STL自带的unsord_map()作为hash表,key是frame_id,value是list内对应元素的指针(迭代器),这样对frame_id对应的元素的操作只需要O(1)级别复杂度。
(对应的leetcode题,代码相似度很高)
//初始化 std::listTASK #2 - BUFFER POOL MANAGER INSTANCEpage_list_; std::unordered_map ::iterator> frame_table_; // 使用示例 auto it = frame_table_.find(frame_id); if (it != frame_table_.end()) { // exist page_list_.erase(it->second); frame_table_.erase(it); }
2.1 frame_id和page_id的关系
- 每一个BPLI(BUFFER POOL MANAGER INSTANCE)都会被分配一个固定的pool_size_,根据pool_size_ 新建的pages_数组实际上就是BPLI用来存放页面的盒子。
- 直观上来看,frame_id就是是pages_数组的下标,而page_id指的放入盒子(数组)的page号。
- 所以replacer管理的是frame_id的分配,BPLI 的工作就是负责分配frame_id(实则是分配盒子)给pages。
2.2 天坑:DeletePgImp() 的返回值问题
这个坑卡了我一下午的时间debug,就一个返回值让我从62分跳到了96分。
按道理找不到页面应该返回false,这里却要求的是true。虽然也的确是审题不当,但这个着实把我坑惨了。
2.3 UnpinPgImp() 注意事项,dirty()测试跑不通
这个函数没有太多注解,所以有的地方都是靠上传测试摸索出来的,感觉实在没必要。
1.对于pin_count_大于1的情况,是减一,而不是清零。
2.不需要对脏页面进行写回磁盘,只需要把脏标记在对应page上就行。
这部分其实看了需求就会知道,是最简单的一部分,只需要对之前两部分进行调用就行了,没有太大问题。
3.1 线程安全
这个管理器不需要设置锁,只需要在replacer和bpli设置锁就行。



