一、使用说明
1.1 项目简介
某一楼有20层,操作者可以通过使用5部电梯从起始楼层来到达指定的楼层。本项目通过实现电梯调度,来模拟实现操作系统的调度过程,并且学习Android环境下使用Java的多线程编程方法以及调度算法。
1.2 项目要求操作者可以在1~20层任意一层进入电梯并到达指定楼层
电梯中有一些按键供操作者使用,开门键(缩短开门时间)、关门键(缩短关门时间)、上行键、下行键(前往上层或下层)
每部电梯外部和内部有一个数码器用于显示当前电梯当前所在楼层
5部电梯相互联结,即当一个电梯按钮按下去时,其它电梯相应按钮同时点亮,表示也按下去了。所有电梯初始状态都在第一层,且每个电梯没有相应请求情况下,在原地保持不动。在电梯到达起始楼层或终点楼层时,会自动进入电梯内部,进入电梯内部后可以在电梯内部等待到达终点楼层,也可以退回到外部界面进行下次操作
电梯运行时每层楼需要1秒,开门或关门时需要5秒
1.3 操作手册
运行程序后,首先进入操作界面,即电梯外部。如图,共有5部电梯,且每部电梯有数码显示器和上下行按键。由于界面大小的限制,将电梯数字按键改为手动输入起始点和终点,位于5部电梯下方。以下只演示一部电梯的使用情况,其余电梯使用情况相同,只需根据调度算法去选择不同电梯来执行相应任务。
1.3.1 情况一:1至15层
第一步,输入起始点和终点。
第二步,点击任一上行按键可启动电梯,目前电梯状态都在初始楼层1楼且都处于静止状态,该情况下默认第一部电梯启动。当电梯到达起始点楼层时,会自动进入电梯内部。
第三步,电梯内部的数码显示器会显示当前楼层。其中刚进入电梯时会有5秒延迟,该时间用于乘客进入电梯,可以提前点击左下方关门按钮让电梯立即启动。由于界面大小问题,无法在保证美观的前提下完全装下20个按键,因此该电梯只需在电梯外输入起始点和终点。
左上角的返回按钮可以让视角返回上一层界面观察电梯调度情况。
界面1
界面2
第四步,当电梯到达指定楼层后,会有5秒延迟来打开电梯门,此时点击开门按键会立刻开门离开电梯,返回电梯操作界面。可以看到此时第一部电梯已到达15层。可以在该界面继续进行其他操作。
1.3.2 情况二:电梯从1层前往20层时,有乘客在10层按键希望到达18层
第一步:输入起始点和终点,点击向上按键,自动进入电梯界面,电梯开始自动向上前进。
界面1
界面2
第二步:点击左上角返回按键,返回上一层,输入另一乘客的请求楼层,起始点10层,终点18层。调度算法会调用同向的第一部电梯。
第三步:电梯到达10层后会自动进入电梯内部,直到到达18层后会离开电梯。
第四步:电梯在将中间乘客送到指定地点后,继续前往原终点20层。
1.4 注意事项输入的起始点和终点必须是1~20的数字,且在按电梯按键时该输入框不可为空,若为空会有提示。
5部电梯会出现满负荷操作现象,即所有电梯都在运行,若此时操作过快会引起系统错误,此时会有提示禁止操作。
在进入电梯内部后,只有在电梯停止过程中,即乘客刚进入电梯的过程和电梯刚到终点等待乘客离开电梯时,才可以使用开门与关门键,其他时间使用电梯不会做出任何反应。
电梯可以接受多个中间请求,但同一中间请求只会有一部电梯响应。如A电梯在从1层前往20层的过程中,如果中间有乘客请求从10层到18层,A电梯会响应该请求,但如果在电梯到达10层前多次按电梯按键,其他电梯不会做出任何反应,只有A电梯会继续前往10层。
二、概述
2.1 基本思路
该电梯调度项目所采取的电梯调度算法与操作系统中进程调度算法中的抢占式SJF调度算法类似,因为SJF调度算法对于给定的一组进程,平均等待时间最小,且抢占式SJF调度算法适用于电梯项目,在电梯上升或下降过程中,可能会出现同方向的楼层发出请求,抢占式SJF调度算法满足该现象,可以使同方向的乘客共用一部电梯。因此采用此算法作为基本思路。
根据抢占式的SJF调度算法,本项目的调度算法的基本思想为三步:
同方向上有至少一个正在赶到乘客所在楼层的电梯,选择距离最近的。根据抢占式SJF调度算法,如果后来请求的进程所需的时间更短,则提前执行该进程,当该进程执行完后,恢复原进程。电梯调度同理,在某个电梯执行某个请求时,如果后来存在该电梯同向上的请求,且电梯正在前往目标楼层时,电梯会响应该请求,并先将该乘客送往目的地后,继续执行原请求。项目中最重要的步骤为此步,严格按照抢占式SJF调度算法实现,平均等待时间最小
若没有同方向的电梯,调度距离最近的空闲电梯。这与抢占式SJF调度算法刚开始的情况相同,并没有进程需要执行,一旦有请求,立马放入执行过程中,无需等待
若既没有同方向也没有闲置的电梯,一直等待到有电梯闲置
2.2 主要文件MainActivity.class & activity_main.xml (主文件,即电梯外部操作界面)
Inner_elavator.class & inner_elavator.xml(电梯内部文件)
TextCircleView.class(数码显示器控件)
Elavator.class(电梯类,相当于进程,既包含了相应的电梯操作,也包含了操作系统中的PCB)
DeletableEditText.class(输入框控件)
三、具体实现
3.1 PCB
在该项目中引用了操作系统中PCB的概念,即进程控制块,其中包含了电梯的一些状态以及一些电梯信息:
3.2 调度算法
由于可以直接得到乘客的起点和终点,电梯可以直接判断乘客的前进方向是向上还是向下。算法中的choice为将要调用的电梯的标号,初始为-1,distance数组为各电梯距离起点的距离。根据抢占式的SJF调度算法,本项目的调度算法的基本思想为三步:
1.同方向上有至少一个正在赶到乘客所在楼层的电梯,选择距离最近的。
根据算法要求,首先遍历五部电梯寻找同向电梯,若有同向,修改电梯距离至起始点的位置,默认情况距离为100。在此之后,对数组distance进行遍历,寻找最短距离,若有同向电梯,将该电梯标志赋给choice。若没有则证明无同向电梯,进行下一步判断空闲电梯的距离。
2.若没有同方向的电梯,调度距离最近的空闲电梯。
若无同方向电梯,经过第一步后choice仍为-1,之后判断各个电梯是否空闲,若空闲则修改其至起点距离,并寻找最短距离,将其电梯标号赋给choice。
3.若既没有同方向也没有闲置的电梯,一直等待到有电梯闲置。此时choice仍为-1,只有在有电梯闲置的时候才可以接受请求。
3.3 电梯方向判断
Elavator类提供的Judge方法可以判断电梯此时的方向,并且能够将初值起点、终点、方向赋给当前电梯。
3.4 电梯执行当前位置到起点的过程
Elavator类提供的Pos_to_Start方法可以让电梯在接受到指令后,从当前位置前往目标楼层的起始点。首先判断当前是否到达指定位置,根据方向不同对该电梯的位置count进行修改,若到达指定位置,即到达起点时,进入电梯内部,转入活动Inner_elavator,同时原操作界面在电梯停止五秒后,判断此时乘客要求电梯向上还是向下,修改电梯方向值。
3.5 电梯调用
Elavator类中的startTimer()方法让系统可以调用该电梯。在电梯不是停止的条件下,向线程发送更新UI的消息,之后根据电梯状态,是否有被其他楼层请求,是否到达起点等情况调用不同的方法。
3.6 电梯接受其他楼层请求
Elavator类中的Add()方法实现了其他楼层可以向同向电梯发出请求并得到接受。对正在调用的电梯的midEnd、midStart进行判断,判断该电梯是否存在中间请求,若存在,电梯停止5秒,留给乘客上下行。
3.7 电梯从起点到终点
Elavator类中的Start_to_End()方法实现了电梯从起点前往终点的过程。在未到达指定位置前,通过修改count来修改电梯当前位置,若到达终点后,停止5秒留给乘客离开电梯,同时判断该电梯是否仍存在中间请求,即有人在该电梯前往终点的路上进入了电梯,并选择了其他楼层作为终点,如存在则继续前往下一个终点。
3.8 电梯内部的实现
电梯内部的实现也是依靠创建一个线程。在进入电梯内部时,同时将起点、终点等信息传入该活动。
首先给1秒钟的思考时间,乘客是否需要快速关门,若关门则取消剩余的等待时间,直接开始运行。在电梯到达之前按下开门键不会有任何反应,只有当电梯到达之后,才会立即退出电梯。
四、总结
这次的项目主要是学习操作系统知识中的进程调度算法,其中涉及的知识点包括进程的创建和状态切换、PCB以及进程调度算法中的SJF调度算法。通过这次项目不仅是对操作系统中这一方面的知识点了解更深,对Android环境下的进程创建和管理有了更多的了解。
由于创建的文件是APP,受制于屏幕的大小,没有将20个数字键全部装入屏幕,而是采取了更为简便的直接输入。这与项目要求略有不符,没有把20个电梯按键全装入,因为初学Android,虽然能实现全部20个电梯按键在电梯内部的功能,但是会出现大量的重复代码(作为初学者的我认为可能需要20个监听?),而且UI做的很丑,希望助教能手下留情。
第一次使用Android和Java写有关进程的算法,项目中可能还存在一些问题,比如有时切换电梯内外时略有延迟,会造成电梯内外数据不同步的现象,而且电梯频繁的内外切换可能会影响观察数据,因为一些开关门的时间问题,很多时候会注意到关门出电梯的时候,该电梯已经前往其他有需求的楼层,这一点由于不同操作者对电梯进行操作是在内部,而外部的进程无法突然中断,所以会造成内外不同步的现象,但是这种现象算是比较少数的,比较多数的情况可能是因为进程开太多了,导致有时楼梯的数字时间是对的,但会一下走2个数字,即用2秒从1楼直接显示3楼,这个问题我不是很懂……所以没能解决。