nachos task1.2
要求:
实现条件变量 , 通过中断使能和不使能两种操作提供条件变量上的原子操作。按照 nachos.threads.Condition, 其中已经实现了一个使用信
号量机制的例子。 在不使用信号量的情况下提供一种等同的实现方法 ( 使用锁 ) 。 实现放在 nachos.threads.Condition2 类中 .
思路: threads.Lock 类 提 供 了 锁 以 保 证 互 斥 . 在 临 界 代 码 区 的 两 端 执 行 Lock.acquire() 和 Lock.release() 即可保证同时只有一个线程访问临界代码区 . 条件变量建立在锁之上 , 由 threads.Condition 实现 , 它是用来保证同步的工具 . 每一个条件变量拥有一个锁变量 ( 该锁变量亦可被执行 acquire 和 release 操作 , 多个条件变量可共享同一个锁变量 ). 当处于临界区内的拥有某锁 L 的当前线程对与锁 L 联系的条件变量执行 sleep 操作时 , 该线程失去锁 L 并被挂起 . 下一个等待锁 L 的线程获得锁 L ( 这个过程由调度程序完成 ) 并进入临界区 . 当拥有
锁 L 的临界区内的当前线程对与锁 L 联系的条件变量执行 wake 操作时 ( 通常调用 wake 之后紧接着就是 Lock.release), 等待在该条件变量上的之多一个被挂起的线程 ( 由调用 sleep 引起 ) 被重新唤醒并设置为就绪状态 . 若执行 wakeall 操作 , 则等待在该条件变量上的所有被
挂起的线程都被唤醒 . threads.condition 已经实现了一个条件变量 ( 采用信号量实现 ), 题目要求用屏蔽 / 禁止中断的方法再实现一下条件变量 ( 写在 threads.condition2 中 )
方案:
只需简单修改 threads.condition 的代码即可 . condition.sleep 采用 waiter.P() 实现休眠 (waitor 是一个信号量 ) 并将 waitor 放入信号量队列 , 在我们的实现中改成用 KThread.sleep() 实现休眠并将当前线程放入线程队列 , 并在 sleep 函数开始 / 结尾处屏蔽 / 允许中断以保证原子性 ;
condition.wake 中从等待信号量队列中取出信号量并对其进行 V 操作实现唤醒 , 在我们的实现中改成从线程队列中取出线程用 KThread.ready() 实现唤醒 ( 同样要在 wake 函数开始 / 结尾处屏蔽 / 允许中断 ), wakeall 函数的实现依赖于 wake. 只需不断地 wake 直到队列为空为止 .
用开关中断的方式来将一个等待该条件变量的线程加入到等待队列中,保证院子操作,临界区用加锁和释放锁的方式来实现院子操作。
首先,需要先定义一个锁变量,一个线程等待队列,一个不需要等待的线程个数。
private Lock conditionLock;
private ThreadQueue waitQueue;
private int value;
然后写 Condition2 的构造方法
public Condition2(Lock conditionLock) {
this.conditionLock = conditionLock;
this.waitQueue=ThreadedKernel.scheduler.newThreadQueue(true);;
this.value=0;
}
当一个线程调用 sleep 方法时,他首先释放拥有的某一个条件变量的锁(这个时候其他的线程可以获得这个锁),然后加入到等待队列里,将自己挂起,直到有人来将他唤醒,他再得到这个锁,进入临界区执行。为保证拜操作的原子性,使用开关中断的方法。
public void sleep() {
Lib.assertTrue(conditionLock.isHeldByCurrentThread());
conditionLock.release();
boolean intStatus = Machine.interrupt().disable();
value ++;
waitQueue.waitForAccess(KThread.currentThread());
KThread.sleep();
Machine.interrupt().restore(intStatus);
conditionLock.acquire();
}
wake 操作是当一个线程使用完临界区时,便唤醒等待在队列中的下一个线程,这也是一个原子操作,需要用到开关中断。
public void wake() {
Lib.assertTrue(conditionLock.isHeldByCurrentThread());
if (value > 0) {
boolean intStatus = Machine.interrupt().disable();
KThread thread = waitQueue.nextThread();
if (thread != null) {
thread.ready();
value --;
}
Machine.interrupt().restore(intStatus);
}
}
wakeall 的思想采用了 condition 的实现方法
public void wakeAll() {
Lib.assertTrue(conditionLock.isHeldByCurrentThread());
KThread thread=waitQueue.nextThread();
while (thread!=null)
wake();
thread=waitQueue.nextThread();
}
最后是测试,测试的话可以将用condition的地方换成condition2.然后和之前是一样的效果就成功了。当然后面的几个任务里也会用到条件变量,也可以在那个时候测试,比如在task1.6里就可以测试。详见后面的博客。
要求:
实现条件变量 , 通过中断使能和不使能两种操作提供条件变量上的原子操作。按照 nachos.threads.Condition, 其中已经实现了一个使用信
号量机制的例子。 在不使用信号量的情况下提供一种等同的实现方法 ( 使用锁 ) 。 实现放在 nachos.threads.Condition2 类中 .
思路: threads.Lock 类 提 供 了 锁 以 保 证 互 斥 . 在 临 界 代 码 区 的 两 端 执 行 Lock.acquire() 和 Lock.release() 即可保证同时只有一个线程访问临界代码区 . 条件变量建立在锁之上 , 由 threads.Condition 实现 , 它是用来保证同步的工具 . 每一个条件变量拥有一个锁变量 ( 该锁变量亦可被执行 acquire 和 release 操作 , 多个条件变量可共享同一个锁变量 ). 当处于临界区内的拥有某锁 L 的当前线程对与锁 L 联系的条件变量执行 sleep 操作时 , 该线程失去锁 L 并被挂起 . 下一个等待锁 L 的线程获得锁 L ( 这个过程由调度程序完成 ) 并进入临界区 . 当拥有
锁 L 的临界区内的当前线程对与锁 L 联系的条件变量执行 wake 操作时 ( 通常调用 wake 之后紧接着就是 Lock.release), 等待在该条件变量上的之多一个被挂起的线程 ( 由调用 sleep 引起 ) 被重新唤醒并设置为就绪状态 . 若执行 wakeall 操作 , 则等待在该条件变量上的所有被
挂起的线程都被唤醒 . threads.condition 已经实现了一个条件变量 ( 采用信号量实现 ), 题目要求用屏蔽 / 禁止中断的方法再实现一下条件变量 ( 写在 threads.condition2 中 )
方案:
只需简单修改 threads.condition 的代码即可 . condition.sleep 采用 waiter.P() 实现休眠 (waitor 是一个信号量 ) 并将 waitor 放入信号量队列 , 在我们的实现中改成用 KThread.sleep() 实现休眠并将当前线程放入线程队列 , 并在 sleep 函数开始 / 结尾处屏蔽 / 允许中断以保证原子性 ;
condition.wake 中从等待信号量队列中取出信号量并对其进行 V 操作实现唤醒 , 在我们的实现中改成从线程队列中取出线程用 KThread.ready() 实现唤醒 ( 同样要在 wake 函数开始 / 结尾处屏蔽 / 允许中断 ), wakeall 函数的实现依赖于 wake. 只需不断地 wake 直到队列为空为止 .
用开关中断的方式来将一个等待该条件变量的线程加入到等待队列中,保证院子操作,临界区用加锁和释放锁的方式来实现院子操作。
首先,需要先定义一个锁变量,一个线程等待队列,一个不需要等待的线程个数。
private Lock conditionLock;
private ThreadQueue waitQueue;
private int value;
然后写 Condition2 的构造方法
public Condition2(Lock conditionLock) {
this.conditionLock = conditionLock;
this.waitQueue=ThreadedKernel.scheduler.newThreadQueue(true);;
this.value=0;
}
当一个线程调用 sleep 方法时,他首先释放拥有的某一个条件变量的锁(这个时候其他的线程可以获得这个锁),然后加入到等待队列里,将自己挂起,直到有人来将他唤醒,他再得到这个锁,进入临界区执行。为保证拜操作的原子性,使用开关中断的方法。
public void sleep() {
Lib.assertTrue(conditionLock.isHeldByCurrentThread());
conditionLock.release();
boolean intStatus = Machine.interrupt().disable();
value ++;
waitQueue.waitForAccess(KThread.currentThread());
KThread.sleep();
Machine.interrupt().restore(intStatus);
conditionLock.acquire();
}
wake 操作是当一个线程使用完临界区时,便唤醒等待在队列中的下一个线程,这也是一个原子操作,需要用到开关中断。
public void wake() {
Lib.assertTrue(conditionLock.isHeldByCurrentThread());
if (value > 0) {
boolean intStatus = Machine.interrupt().disable();
KThread thread = waitQueue.nextThread();
if (thread != null) {
thread.ready();
value --;
}
Machine.interrupt().restore(intStatus);
}
}
wakeall 的思想采用了 condition 的实现方法
public void wakeAll() {
Lib.assertTrue(conditionLock.isHeldByCurrentThread());
KThread thread=waitQueue.nextThread();
while (thread!=null)
wake();
thread=waitQueue.nextThread();
}
最后是测试,测试的话可以将用condition的地方换成condition2.然后和之前是一样的效果就成功了。当然后面的几个任务里也会用到条件变量,也可以在那个时候测试,比如在task1.6里就可以测试。详见后面的博客。