时间片轮转法(RR)go语言实现

487 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

关于时间片轮转法,是规定一个较小的时间片。在就绪队列中,每个进程循环使用该时间片。当一个进程使用完时间片以后,就要回到就绪队列尾部,cpu交给下一个进程。使用时间片轮转法是抢占式的。

具体流程

5d745ccf9edd4a728e09ef855ab31383_tplv-k3u1fbpfcp-watermark.png

如图,这里假设时间片是1。首先cpu本着先来先服务的原则(FCFS)把进程添加到就绪队列中,A,B,C,D,E按序到达。

image.png

就绪队列队首的进程A出队列,并把cpu分配给进程A,进程A可以使用cpu

image.png

进程A使用完一个时间片以后,交出cpu使用权,并回到就绪队列尾部。下一个进程B出队列,并把cpu分配给进程B

image.png

当一个进程需要的服务时间已经全部执行完了以后,它便不会回到就绪队列里面。因为它已经执行完了嘛。这样循环往复,直到所有进程执行结束。就会得到上面的答案。这里还有一个点需要注意的。如果一个时间片还未使用完毕,但是进程已经执行完了,不需要等到时间片全部消耗完,直接让出cpu使用权给队列的下一个进程。

go实现

package main

import (
   "fmt"
   "sort"
)


/**
 * 进程控制块
 */
type Pcb struct {
   Name string
   //这里我没有用上这个
   State int
   /** 到达时间 */
   ArriveTime int
   /** 已使用cpu时间 */
   RunTime int
   /** 服务时间 */
   ServiceTime int
   /** 结束时间 */
   FinishTime int
}


/* 就绪队列 */
var readyQueue []*Pcb
/* 用于记录结束的pcb */
var finishQueue []*Pcb
/** 用来记录当前时间 */
var currentTime int = 0
/** 时间片 */
var LimitTime int





//时间片轮转法
func Rr() {
   //所有进程按到达时间到达
   sort.Sort(SortPcbByArriveTime(readyQueue))
   //当前时间块进到第一个到达的进程的时间
   currentTime = readyQueue[0].ArriveTime
   for len(readyQueue) > 0{
      //就绪队列中取出第一个进程
      nowProcess := readyQueue[0]
      readyQueue = readyQueue[1:]
      //使用时间片
      if nowProcess.ServiceTime - nowProcess.RunTime > LimitTime {
         //如果需要使用时间小于时间片,减小该进程运行时间
         nowProcess.RunTime = nowProcess.RunTime + LimitTime
         //更新当前时间
         currentTime = currentTime + LimitTime
         //把当前为执行完的进程插入到队列尾部
         readyQueue = append(readyQueue, nowProcess)
      } else {
         //如果进程在没有使用完时间片以前已经结束

         //更新当前时间
         currentTime = currentTime + nowProcess.ServiceTime - nowProcess.RunTime
         nowProcess.RunTime = nowProcess.ServiceTime
         //更新结束时间
         nowProcess.FinishTime = currentTime
         finishQueue = append(finishQueue,nowProcess)
      }
   }
}


func FinishPrint() {
   for i:=0;i<len(finishQueue);i++ {
      // 周转时间
      p := finishQueue[i]
      turnaroundTime := p.FinishTime - p.ArriveTime
      weightWaitTime := float64(turnaroundTime) / float64(p.ServiceTime)
      t := fmt.Sprintf("进程名称:%s 到达时间:%d 结束时间:%d 周转时间:%d 带权周转时间:%f\n",
         p.Name,p.ArriveTime,p.FinishTime,turnaroundTime,weightWaitTime)
      fmt.Println(t)
   }
}




/**
 * 用于对pcb列表进行排序
 * 根据开始时间
 */
type SortPcbByArriveTime []*Pcb

func (s SortPcbByArriveTime)Len()int {
   return len(s)
}

func (s SortPcbByArriveTime)Less(a int, b int)bool {
   return s[a].ArriveTime < s[b].ArriveTime
}

func (s SortPcbByArriveTime)Swap(a int, b int) {
   tmp := s[a]
   s[a] = s[b]
   s[b] = tmp
}




func main() {

   //时间片
   LimitTime = 1
   //进程的队列
   readyQueue = []*Pcb{
      {Name:"A", ArriveTime:0, ServiceTime:4,},
      {Name:"B", ArriveTime:1, ServiceTime:3,},
      {Name:"C", ArriveTime:2, ServiceTime:4,},
      {Name:"D", ArriveTime:3, ServiceTime:2,},
      {Name:"E", ArriveTime:4, ServiceTime:4,},
   }

   //时间片轮转
   Rr()
   FinishPrint()
}

附上一个运行结果,成功了捏。

image.png

最后进程E的带权周转时间很明显3.25,用周转时间除服务时间,得到的结果。书上给的3.33,我暂时不好说。

关于自己遇到的一个坑

这个时间片轮转法是我在操作系统实验课上写的。事实上,我一开始并不是这样想的。因为上述题目中A,B,C,D,E不是一下子全部都在就绪队列里面的嘛。 比如,A在使用时间片的时候,B还未在就绪队列里面,B应该是在时刻1的时候才进入就绪队列里面。如果这样想的话,计算出来的结果就会和书上的不一样。可能书上不要求这么复杂吧。

image.png

在时刻1-2之间,B在使用cpu,A回到了就绪队列尾部,然后C还未进入就绪队列。当到达时刻3的时候,C才会到就绪队列尾部。这样的话,并不向上面的那样简单。

附上这种情况的时间片轮转的go实现

package main

import (
   "fmt"
   "sort"
)

/**
 * 进程控制块
 */
type Pcb struct {
   Name string
   State int
   /** 到达时间 */
   ArriveTime int
   /** CPU使用时间 */
   RunTime int
   /** 还需要运行的时间 */
   ServiceTime int
   /** 结束时间 */
   FinishTime int
}


/* 用于存储输入的pcb */
var pcbList []*Pcb
/* 就绪队列 */
var readyQueue []*Pcb
/* 用于记录结束的pcb */
var finishQueue []*Pcb
/** 用来记录当前时间 */
var currentTime int = 0
/** 时间片 */
var limitTime int




//时间片轮转法
func Rr() {
   //所有pcb按到达时间到达
   sort.Sort(SortPcbByArriveTime(pcbList))

   for len(pcbList) > 0 || len(readyQueue) > 0{
      if len(readyQueue) != 0 {
         //就绪队列中取出第一个进程
         nowPcb := readyQueue[0]
         readyQueue = readyQueue[1:]
         //使用时间片
         if nowPcb.ServiceTime - nowPcb.RunTime > limitTime {
            //如果需要使用时间小于时间片,减小该进程运行时间
            nowPcb.RunTime = nowPcb.RunTime + limitTime
            //更新当前时间
            currentTime = currentTime + limitTime
            //判断队列里面是否有进程需要进入就绪队列
            toReadyList()
            //把当前为执行完的进程插入到队列尾部
            readyQueue = append(readyQueue, nowPcb)
         } else {
            //如果在未使用完时间片,进程已经结束
            currentTime = currentTime + nowPcb.ServiceTime - nowPcb.RunTime
            nowPcb.RunTime = nowPcb.ServiceTime
            //更新结束时间
            nowPcb.FinishTime = currentTime

            finishQueue = append(finishQueue,nowPcb)
            //判断队列里面是否有进程需要进入就绪队列
            toReadyList()
         }
      } else {
         //如果就绪队列为空,更新当前时间,并添加进程到就绪队列
         currentTime = pcbList[0].ArriveTime
         toReadyList()
      }
   }
}


func FinishPrint() {

   for i:=0;i<len(finishQueue);i++ {
      // 周转时间
      p := finishQueue[i]
      turnaroundTime := p.FinishTime - p.ArriveTime
      weightWaitTime := float64(turnaroundTime) / float64(p.ServiceTime)
      t := fmt.Sprintf("进程名称:%s 到达时间:%d 结束时间:%d 周转时间:%d 带权周转时间:%f\n",
         p.Name,p.ArriveTime,p.FinishTime,turnaroundTime,weightWaitTime)
      fmt.Println(t)
   }
}

/**
 * 把到达的pcb加入到就绪队列中
 */
func toReadyList() {
   for len(pcbList) > 0{
      if pcbList[0].ArriveTime <= currentTime {
         readyQueue = append(readyQueue, pcbList[0])
         pcbList = pcbList[1:]
      } else {
         break
      }
   }
}



/**
 * 用于对pcb列表进行排序
 * 根据开始时间
 */
type SortPcbByArriveTime []*Pcb

func (s SortPcbByArriveTime)Len()int {
   return len(s)
}

func (s SortPcbByArriveTime)Less(a int, b int)bool {
   return s[a].ArriveTime < s[b].ArriveTime
}

func (s SortPcbByArriveTime)Swap(a int, b int) {
   tmp := s[a]
   s[a] = s[b]
   s[b] = tmp
}



func main() {

   //时间片
   limitTime = 1
   //进程的队列
   pcbList = []*Pcb{
      {Name:"A", ArriveTime:0, ServiceTime:4,},
      {Name:"B", ArriveTime:1, ServiceTime:3,},
      {Name:"C", ArriveTime:2, ServiceTime:4,},
      {Name:"D", ArriveTime:3, ServiceTime:2,},
      {Name:"E", ArriveTime:4, ServiceTime:4,},
   }

   //时间片轮转
   Rr()
   FinishPrint()
}

运行结果

image.png

和书上的答案不一致,但是也不算错,就是复杂了一点