一个任务队列:
struct BGItem { void* arg; void (*function)(void*); }; //用的是deque双向链表作为底层的数据结构,按时间顺序执行任务,没有设立优先级队列 typedef std::deque
BGQueue; BGQueue queue_;
主进程一旦判定需要进行compact操作,就把compact任务压进队列queue_中,BGItem是存有任务函数和db对象指针的结构。而后台进程从一开始就不断根据队列中的函数指针执行compact任务。BGThread()函数就是不停的在queue_中取出函数指针,执行。
void PosixEnv::Schedule(void (*function)(void*), void* arg) { PthreadCall("lock", pthread_mutex_lock(&mu_)); // Start background thread if necessary //开启后台进程,如果没有开启 if (!started_bgthread_) { started_bgthread_ = true; PthreadCall( "create thread", pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); } // If the queue is currently empty, the background thread may currently be // waiting. if (queue_.empty()) { PthreadCall("signal", pthread_cond_signal(&bgsignal_)); } // Add to priority queue //将任务压入队列 queue_.push_back(BGItem()); queue_.back().function = function; queue_.back().arg = arg; PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } void PosixEnv::BGThread() { while (true) { // Wait until there is an item that is ready to run PthreadCall("lock", pthread_mutex_lock(&mu_)); while (queue_.empty()) { PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); } //从队列取出函数,执行 void (*function)(void*) = queue_.front().function; void* arg = queue_.front().arg; queue_.pop_front(); PthreadCall("unlock", pthread_mutex_unlock(&mu_)); (*function)(arg); } }
后台进程一直执行queue_中的任务,由于queue_是动态的,自然需要考虑queue_空了怎么办,leveldb采用的是条件量pthread_cond_t bgsignal_,队列空了就进入等待,直至主进程有新的任务加入进来,而条件变量一般是要和pthread_mutex_t mu_搭配使用,防止某些逻辑错误。核心函数有:
pthread_mutex_init(),pthread_cond_init()条件变量、互斥锁的初始化 pthread_mutex_lock(),pthread_mutex_unlock()关锁解锁 pthread_cond_signal(),pthread_cond_wait()条件变量成立,等待条件成立 pthread_create创建子线程
此外PosixEnv中还有FileExists、GetChildren、DeleteFile、CreateDir、DeleteDir、GetFileSize、RenameFile等等函数,它们的作用就如它们的名字所说一样。
好了,从env给我的收获就是:
利用虚基类的特性提供了默认的实现,也开放了用户自定义操作的权限 面向对象编程范式的学习,把一切操作定义成类 文件的加锁解锁,线程的同步 C的文件流操作,对文件名的字符提取操作,创建、删除文件和路径,这些都可以直接用到将来自己的项目中