Task 1.3 Alarm
- 实验要求
- 实验关键代码
- 试验测试代码
- 关键代码分析
- 测试结果分析
实验要求
◆ Complete the implementation of the Alarm class, by implementing the waitUntil(long x) method.
◆ A thread calls waitUntil to suspend its own execution until time has advanced to at least now + x.
◆ This is useful for threads that operate in real-time, for example, for blinking the cursor once per second.
◆ There is no requirement that threads start running immediately after waking up; just put them on the ready queue in the timer interrupt handler after they have waited for at least the right amount of time.
◆ Do not fork any additional threads to implement waitUntil();
◆ you need only modify waitUntil() and the timer interrupt handler.
◆ waitUntil is not limited to one thread;
◆ any number of threads may call it and be suspended at any one time.
实验关键代码
变量声明
private static LinkedList<WaitForAlarmThread> waitForAlarmThreadList ;
方法实现
关键代码在waitForUntil(long x)方法和timerInterrupt()方法。
1、Void waitUntil(long x)方法:
public void waitUntil(long x) {
// for now, cheat just to get something working (busy waiting is bad)
boolean preState = Machine.interrupt().disable();//关中断
long wakeTime = Machine.timer().getTime()+x;//计算唤醒的时间
WaitForAlarmThread waitForAlarmThread = new WaitForAlarmThread(wakeTime, KThread.currentThread());
waitForAlarmThreadList.add(waitForAlarmThread);//将线程加入到等待链表中
KThread.sleep();//让该线程睡眠
Machine.interrupt().restore(preState);//恢复中断
}
2、WaitForAlarmThread类【包含线程和唤醒时间的数据结构】
class WaitForAlarmThread{
long wakeTime;
KThread thread;
public WaitForAlarmThread(long wakeTime,KThread thread){
this.wakeTime=wakeTime;
this.thread=thread;
}
}
3、Void timerInterrupt()方法:
public void timerInterrupt() {
boolean preState = Machine.interrupt().disable();//关中断
WaitForAlarmThread x;
for(java.util.Iterator i = waitForAlarmThreadList.iterator();i.hasNext();){
x = (WaitForAlarmThread)i.next();//取出链表中的每个线程判断是否达到唤醒时间
if(x.wakeTime<=Machine.timer().getTime()){//如果达到唤醒时间,将其从链表中移除并唤醒该线程
i.remove();
x.thread.ready();
}
}
Machine.interrupt().restore(preState);//恢复中断
}
实验测试代码
public static void AlarmTest(){
KThread a = new KThread(new Runnable() {
public void run() {
System.out.println("线程1启动");
for(int i = 0;i<5;i++){
if(i == 2){
System.out.println("线程1要暂时隐退,此时时间为:"+Machine.timer().getTime()+",大约1700clock ticks之后再见");
new Alarm().waitUntil(800);
System.out.println("线程1回来了,此时时间为:"+Machine.timer().getTime());
}
System.out.println("*** thread 1 looped "
+ i + " times");
KThread.currentThread().yield();
}
}
});
a.fork();
for(int i = 0;i<5;i++){
if(i == 2){
System.out.println("线程0要暂时隐退,此时时间为:"+Machine.timer().getTime()+",大约800clock ticks之后再见");
new Alarm().waitUntil(1700);
System.out.println("线程0回来了,此时时间为:"+Machine.timer().getTime());
}
System.out.println("*** thread 0 looped "
+ i + " times");
KThread.currentThread().yield();
}
}
关键代码分析
思路分析
要想实现void waitForUntil (long x)方法,要将当前线程保存起来,记录线程唤醒时间,然后在达到唤醒时间时将其唤醒。
新建一个数据结构含有变量KThread线程和唤醒时间【在本实验中为waketime】,每次调用waitForUntil(long x)方法时,会将调用线程包装成新建数据结构的对象,并将其加入等待队列中,当到达唤醒时间时将其唤醒即可。
方法解释
1、Void waitForUntil (long x) 方法
通过参数与当前时间算出唤醒时间,将线程与唤醒时间包装起来,加入waitForAlarmThreadList中,然后让该线程睡眠。
2、void timerInterrupt ( ) 方法
这个方法大约每500 clock ticks执行一次,所以每次检查waitForAlarmThreadList 中的所有线程,如果线程的唤醒时间达到,则将其唤醒。
关键点和难点
关键点:知道如何将待唤醒的线程保存起来,使用数据结构将线程和唤醒时间绑在一起。
难点:理解void timerInterrupt()方法大约每500 clock ticks 执行一次。
测试结果分析
测试结果截图
测试结果分析
线程0(在测试代码中为main线程)先执行一次,然后yield;
然后线程1(在测试代码中为a线程)执行一次,如此反复,直到当线程0执行第三次的时候,线程0调用了waitUntil(1700)方法,所以线程0会在大约1700 clock ticks 后继续执行,当线程1执行第三次时,也调用了waitForUntil()方法,参数为800,故线程1会在大约800 clock ticks 之后继续执行,通过看控制台打印结果,可知编写正确。