一、写在前面
上一节我们分析了线程调度器Scheduler类和线程切换函数SWITCH与线程生命周期逻辑函数ThreadRoot函数,了解了线程从创建到销毁的过程与线程的寄存器信息是如何保存与恢复的.在本节我们将分析该包下最后的两个类.
二、源码分析
1、信号量:Semaphore
class Semaphore {
public:
Semaphore(const char* debugName, int initialValue); // set initial value
~Semaphore(); // de-allocate semaphore
char* getName() { return name;} // debugging assist
void P(); // these are the only operations on a semaphore
void V(); // they are both *atomic*
private:
char* name; // useful for debugging
int value; // semaphore value, always >= 0
List *queue; // threads waiting in P() for the value to be > 0
};
信号量是为了解决同步/互斥问题而产生的,信号量的存在能够避免并发线程的执行顺序出现异常从而得到预期之外的效果.Semaphore类中的数据成员有name,value和queue三个,name是用于进行调式的信号量名称,value为信号量目前的取值,queue为在该信号量上阻塞的线程列表.
信号量提供了两个基本的方法P和V,P函数代表请求资源的操作,如果资源不足则使该线程阻塞在该信号量中的等待队列中.V函数代表释放资源的操作,如果阻塞队列中有线程在等待则取出一个后将其唤醒.
Semaphore类中还提供了getName方法用于获取调试名称.
Semaphore::Semaphore(const char* debugName, int initialValue)
{
name = (char*)debugName;
value = initialValue;
queue = new List;
}
Semaphore::~Semaphore()
{
delete queue;
}
void
Semaphore::P()
{
IntStatus oldLevel = interrupt->SetLevel(IntOff); // disable interrupts
while (value == 0) { // semaphore not available
queue->Append((void *)currentThread); // so go to sleep
currentThread->Sleep();
}
value--; // semaphore available,
// consume its value
(void) interrupt->SetLevel(oldLevel); // re-enable interrupts
}
void
Semaphore::V()
{
Thread *thread;
IntStatus oldLevel = interrupt->SetLevel(IntOff);
thread = (Thread *)queue->Remove();
if (thread != NULL) // make thread ready, consuming the V immediately
scheduler->ReadyToRun(thread);
value++;
(void) interrupt->SetLevel(oldLevel);
}
Semaphore的构造函数接收一个字符数组作为调试名称和一个整型值作为该信号量代表的资源的初始值.然后构造一个列表用于存放阻塞的线程.析构函数中将阻塞队列释放.
进入P函数后首先关中断确保P操作是原子的,然后进入while循环,在信号量的value值等于0是不断重复,在每