nachos-java Task1.5 Priority Scheduling

本文详细介绍了优先级调度算法的实现原理与过程,包括实验要求、关键代码、测试代码及结果分析等内容。重点讲解了优先级倒置问题的解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Task1.5 Priority Scheduling

本Task较为复杂,但只要思路清晰,知道每个方法的用途便可以轻松编写,下面我以五个方面来阐述它:

- 实验要求
- 实验关键代码
- 实验测试代码
- 代码分析
- 测试结果分析

实验要求

  • 通过完成PriorityScheduler类来实现优先级调度;
  • 为了使用优先级调度,你需要在 nachos.conf 文件中更改一行内容,需要将ThredededKernel.scheduler的值由nachos.threads.RoundRobinScheduler更改为nachos.threads.PriorityScheduler;
  • 优先级调度应该选择一个有效优先级最高的线程出队列,如果多个线程有效优先级相同,则应该选择等待时间最久的;
  • 优先级调度中需要处理优先级倒置问题(可以通过优先级捐赠的方法解决);

实验关键代码

变量声明

  • PriorityQueue类
protected KThread lockHolder = null; //队列头
protected LinkedList<KThread> waitList = new LinkedList<KThread>();
  • ThreadState类
protected int effectivePriority = -2;//有效优先级初始化为-2
protected final int invalidPriority = -1;//无效优先级
protected HashSet<nachos.threads.PriorityScheduler.PriorityQueue> acquired = new HashSet<nachos.threads.PriorityScheduler.PriorityQueue>();//等待该线程的所有优先队列(每个优先队列里有等待线程),包括等待锁,等待join方法的队列

实现方法

PriorityQueue类
  • waitForAccess()方法
public void waitForAccess(KThread thread) {
        Lib.assertTrue(Machine.interrupt().disabled());
        getThreadState(thread).waitForAccess(this);
}
  • acquire(KThread thread)方法
public void acquire(KThread thread) {
        Lib.assertTrue(Machine.interrupt().disabled());
        getThreadState(thread).acquire(this);
}
  • nextThread()方法
public KThread nextThread() {
        Lib.assertTrue(Machine.interrupt().disabled());
        ThreadState x = pickNextThread();//下一个选择的线程
        if(x == null)//如果为null,则返回null
            return null;
        KThread thread = x.thread;
        getThreadState(thread).acquire(this);//将得到的线程改为this线程队列的队列头
        return thread;//将该线程返回
    }
  • pickNextThread()方法
protected ThreadState pickNextThread() {
    java.util.Iterator i = waitList.iterator();
    KThread nextthread;
    if(i.hasNext()){
    nextthread = (KThread)i.next();//取出下一个线程
    //System.out.println(nextthread.getName());
    KThread x = null;
    while(i.hasNext()){//比较线程的有效优先级,选出最大的,如果优先级相同,则选择等待时间最长的
        x = (KThread)i.next();
        //System.out.println(x.getName());
        int a = getThreadState(nextthread).getEffectivePriority();
        int b = getThreadState(x).getEffectivePriority();
        if(a<b){
                nextthread = x;     
        }
    }
    return getThreadState(nextthread);
    }else 
    return null;
}

ThreadState类
  • getEffectivePriority()方法
public int getEffectivePriority() {
    Lib.assertTrue(Machine.interrupt().disabled());
    if(effectivePriority == invalidPriority&&!acquired.isEmpty()){
        effectivePriority = priority;//先将自己的优先级赋给有效优先级
        for(Iterator i = acquired.iterator();i.hasNext();){//比较acquired中的所有等待队列中的所有线程的优先级
            for(Iterator j = ((PriorityQueue)i.next()).waitList.iterator();j.hasNext();){
                ThreadState ts = getThreadState((KThread)j.next());
                if(ts.priority>effectivePriority){
                    effectivePriority = ts.priority;

                }
            }
        }

        return effectivePriority;
    }else{ 

        if(effectivePriority==-2){ //表明该优先级线程队列不存在优先级捐赠
            return priority;
        }
         return effectivePriority;//如果该线程没有执行,那么它之前算的有效优先级不必重新再算一遍
    }
}
  • waitForAccess(PriorityQueue waitQueue)方法
public void waitForAccess(PriorityQueue waitQueue) {
    waitQueue.waitList.add(this.thread);//将调用线程加入到等待队列
}
  • acquire(PriorityQueue waitQueue)方法
public void acquire(PriorityQueue waitQueue) {
    waitQueue.waitList.remove(this.thread);//如果这个队列中存在该线程,删除
    waitQueue.lockHolder = this.thread;//对于readyQueue来讲,lockHolder为执行线程;对于Lock类的waitQueue来讲,lockHolder为持锁者;对于waitForJoin队列来讲,lockHolder为执行join方法的线程。
    if(waitQueue.transferPriority){//如果存在优先级翻转,则执行下面操作
        this.effectivePriority = invalidPriority;
        acquired.add(waitQueue);//将等待该线程的队列加入该线程的等待队列集合中
    }
}   

实验测试代码

private static class PingTest implements Runnable {
    PingTest(int which) {
        this.which = which;
    }

    public void run() {
        for (int i=0; i<5; i++) {
        System.out.println("*** thread " + which + " looped "
                   + i + " times");
        KThread.currentThread().yield();
        }
    }

    private int which;
    }
public static void PriorityTest(){
    boolean status = Machine.interrupt().disable();//关中断,setPriority()函数中要求关中断
    final KThread a = new KThread(new PingTest(1)).setName("thread1");
    new PriorityScheduler().setPriority(a,2);
    System.out.println("thread1的优先级为:"+new PriorityScheduler().getThreadState(a).priority);
    KThread b = new KThread(new PingTest(2)).setName("thread2");
    new PriorityScheduler().setPriority(b,4);
    System.out.println("thread2的优先级为:"+new PriorityScheduler().getThreadState(b).priority);
    KThread c = new KThread(new Runnable(){
        public void run(){
            for (int i=0; i<5; i++) {
                if(i==2) 
                    a.join();
                System.out.println("*** thread 3 looped "
                           + i + " times");
                KThread.currentThread().yield();
            }
        }
    }).setName("thread3");
    new PriorityScheduler().setPriority(c,6);
    System.out.println("thread3的优先级为:"+new PriorityScheduler().getThreadState(c).priority);
    a.fork();
    b.fork();
    c.fork();
    Machine.interrupt().restore(status);
}

代码分析

思路分析

  所谓线程调度,以我的个人理解,核心就是怎样选择就绪队列中下一个要执行的线程。本实验通过有效优先级比较,选择有效优先级高的为下一个执行的线程;除此之外,本实验要考虑优先级倒置的问题,优先级倒置问题可以通过捐献优先级来解决。

方法解释

名词解释
  • 队列头(lockHolder):每个队列都有一个队列头,该队列头不在队列里,但只有这个队列头先执行,队列里的线程才有可能执行。

  • 无效优先级(invalidPriority):每次调用该方法acquire(PriorityQueue waitQueue)时,this线程的有效优先级就会设置为无效优先级;

PriorityQueue类
  • void waitForAccess(KThread thread)方法

    将参数中线程加入到this队列中,代码实现中调用了ThreadState类中的waitForAccess(PriorityQueue waitQueue)方法。

  • void acquire(KThread thread)方法

    方法解释

    激活this队列(参数中的线程会成为this队列的队列头,但并不在队列里面);

    代码实现解释

    内部代码调用了ThreadState类中的acquire(KThread thread)方法;

    使用范围

    这个方法仅仅当this队列中没有元素时才可能调用。比如当一个线程正在申请没有线程正在等待的锁时,会调用该方法。

    查看代码会发现,有三个地方使用了该方法:
    1、 第一处是readyQueue,当nachos系统第一个线程执行时,会调用readyQueue.acquire(this);
    2、第二处是当调用锁的acquire方法时,如果该锁没有持锁者(即没有线程等待该锁时)会调用;
    3、第三处为KThread的join()方法,当调用join()方法时,会执行waitForJoinQueue.acquire(this);

  • KThread nextThread()方法

    方法解释

    决定this队列中下一个最可能执行的线程【如果this是readyQueue,则选择是下一个执行的线程】,最可能执行的线程是队列中有效优先级最高的线程。

    代码实现解释

    首先调用pickNextThread()方法来得到this队列下一个最可能执行的线程,如果返回为空,则该方法也返回空;如果不为空,则将返回的线程(假设线程A)设置为该队列的队列头【调用ThreadState类中的acquire(PriorityQueue q)方法】,最后返回线程A即可。

  • ThreadState pickNextThread()方法

    方法解释:选择下一个有效优先级最高的线程;

    代码实现解释

    扫描this队列中的每一个线程,通过比较每个线程的有效优先级,来选出最大有效优先级对应的线程。根据题意如果有效优先级相同,则应该选择等待时间最长的线程。对于这一点,我没有设置每个线程的等待时间,因为我的线程队列的底层实现是LinkedList,所以相同有效优先级,只要优先选择该链表前面的线程即可解决这个问题。(在代码中是这样实现的,只有当后一个线程的有效优先级大于前一个线程的有效优先级,变量nextThread的值才会改变)。

ThreadState类
  • int getEffectivePriority()方法

    方法解释

    该方法通过比较this线程的acquired中所有线程的优先级,将最高优先级捐献给this线程。【acquired为一个HashSet容器,该容器中装有所有等待该线程的队列,这些队列包括锁队列(如果该线程持有几把锁,那么等待该锁的线程队列便会在里面),waitForJoin队列(如果A线程join了该线程,则A线程会进入该线程waitForJoin队列)等,队列里放着等待的线程】

    代码实现解释

    通过this线程有效优先级的值来进行分支处理。

    为了防止每次都对等待this线程的容器进行遍历,进行简单优化:

    1、 如果线程的有效优先级等于invalidPriority且acquired容器不为空,则开始扫描容器中的每一个线程,将线程中最大的优先级赋给this线程的有效优先级,然后返回该有效优先级;
    2、如果this线程的有效优先级等于-2(每个线程的初始有效优先级均为-2),证明所比较的队列不允许优先级捐赠,则返回this线程的优先级即可;
    3、如果this线程的有效优先级不是invalidPriority,也不是-2,即证明this线程的有效优先级之前已经计算过,直接返回该线程的有效优先级即可。

  • void waitForAccess(PriorityQueue waitQueue)方法

    将this线程加入到waitQueue中。

  • void acquire(PriorityQueue waitQueue)方法

    方法解释:设置waitQueue的队列头为this线程。【在代码实现中队列头为lockHolder】

    使用范围: 只有两处使用到该方法
    a、PriorityQueue类中acquire(KThread thread)方法调用了该方法;
    b、PriorityQueue类中nextThread()方法调用了该方法。

    代码实现解释

    先将this线程从waitQueue中移除(队列头不能在队列里),在代码中使用remove(this)方法,如果this线程本来就不在waitQueue队列中则返回false,否则返回true;

    然后将队列的队列头更换为this线程;判断waitQueue队列是否允许优先级捐赠,如果允许,则将this线程的有效优先级设置为无效优先级,并将该队列加入到acquired容器中。否则不作处理【即如果不允许优先级捐赠,则有效优先级仍然为初始值-2】。

关键点和难点

  • 关键点

    理解线程调度的概念;
    
  • 难点

    理解PriorityQueue类中acquire方法和ThreadState类中的acquire方法的区别;
    
    怎样来保存等待线程的那些队列,在本实验中采取了HashSet容器;
    
    如何做到避免重复计算有效优先级,通过设置无效优先级来处理;
    
    如何让不允许优先级捐赠的队列避免计算有效优先级,通过设置有效优先级的初始值可以解决;
    
    理解优先级倒置问题的核心,并理解怎样解决。
    

测试结果分析

  • 测试结果截图
    这里写图片描述

  • 测试结果解释

    使用join()方法来对编写的程序进行测试。

    首先给三个线程分别设置了优先级为2,4,6;如果不执行join()方法,必定会按照优先级高低顺序执行(线程3先执行,然后执行线程2,最后执行线程1);但在测试程序中当优先级为6的线程(线程3)执行到第3次时,调用了a.join()方法;如果没有发生优先级捐赠的话,线程2应该先执行,因为它的优先级是4,但由于发生了优先级捐赠,线程3将优先级捐赠给了线程1,所以线程1的有效优先级现在是6,故结果中出现了线程1先执行,然后线程3继续执行,最后线程2才执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值