第一次作业
同步块
RequestList是输入进程Input和调度器进程Manager之间的临界区,主要用于存储输入的请求。
EleState是调度器进程Manager和电梯进程Elevator之间的临界区,主要用于存储电梯的状态。
Input中的锁
向RequestList中插入请求时,锁RequestList
while (true) {
request = elevatorInput.nextPersonRequest();
synchronized (requestList) {
if (request == null) {
requestList.setNoMoreInput();
requestList.notifyAll();
break;
} else {
requestList.offer(request);
requestList.notifyAll();
}
}
}
Manager中的锁
从RequestList中读取请求时,锁RequestList
调度算法运行时,需要查询电梯状态,锁EleState
synchronized (requestList) { //读取输入请求
//退出程序
if (requestList.isNoMoreInput() && requestList.isEmpty()
&& requests.isEmpty() && eleState.isALlEmpty()) {
//设定电梯退出
}
//读取新输入的请求
if (requestList.isEmpty() && requests.isEmpty() && eleState.isALlEmpty()) {
try {
requestList.wait();
} catch (InterruptedException e) { /*nothing*/ }
}
while (!requestList.isEmpty()) {
......
}
}
synchronized (eleState) {
....... //电梯调度
eleState.awaken(); //转入电梯运行
try { eleState.wait(); } catch (InterruptedException e) { /*nothing*/ }
}
Elevator中的锁
电梯运行时,需要改变电梯状态,锁EleState
synchronized (eleState) {
//退出程序
if (eleState.getState() == 0 && eleState.isCanInt()) {
break;
}
this.move(); //电梯运行
eleState.awaken(); //切换Manager进程
try { eleState.wait(); } catch (InterruptedException e) { /*nothing*/ }
}
第二次作业
同步块
RequestList是输入进程InputHandler和调度器进程Scheduler之间的临界区,主要用于存储输入的请求。
ElevatorCache是调度器进程Scheduler和电梯进程Elevator之间的临界区,主要用于存储电梯的状态。
InputHandler中的锁
向RequestList中插入请求时,锁RequestList
while (true) {
request = elevatorInput.nextRequest();
synchronized (RequestList.getIt()) {
if (request == null) { //指令输入结束
RequestList.getIt().setNoMoreInput();
RequestList.getIt().notifyAll();
break;
} else { //指令传入RequestList
RequestList.getIt().offer(request);
RequestList.getIt().notifyAll();
}
}
}
Scheduler中的锁
从RequestList中读取请求时,锁RequestList
向ElevatorCache中插入乘客时,锁ElevatorCache
synchronized (RequestList.getIt()) { //读取输入请求
...... //退出程序
if (!RequestList.getIt().isNoMoreInput() &&
RequestList.getIt().isEmpty() && requests.isEmpty()) {
try {
RequestList.getIt().wait();
} catch (InterruptedException e) { /*nothing*/ }
}
//读取,直到RequestList为空
while (!RequestList.getIt().isEmpty()) {
......
}
} //synchronized_END
elevatorCache = elevatorCaches.get(minAddress);
synchronized (elevatorCache) {
PersonRequest request1 = (PersonRequest) request;
elevatorCache.offerPerson(
new Person(
request1.getPersonId(), request1.getFromFloor(),
request1.getToFloor()));
elevatorCache.notifyAll();
}
Elevator中的锁
电梯调用ElevatorCache时,锁ElevatorCache
synchronized (cache) {
if (cache.isCanInt() && cache.isEmpty()
&& floor == destination && persons.isEmpty()) {
return;
} else if (cache.isEmpty() && floor == destination && persons.isEmpty()) {
try {
cache.wait();
} catch (InterruptedException e) { /*nothing*/ }
}
}
第三次作业
同步块(与第二次作业相同)
RequestList是输入进程InputHandler和调度器进程Scheduler之间的临界区,主要用于存储输入的请求。
ElevatorCache是调度器进程Scheduler和电梯进程Elevator之间的临界区,主要用于存储电梯的状态。
InputHandler中的锁
向RequestList中插入请求时,锁RequestList
while (true) {
request = elevatorInput.nextRequest();
synchronized (RequestList.getIt()) {
if (request == null) { //指令输入结束
RequestList.getIt().setNoMoreInput();
RequestList.getIt().notifyAll();
break;
} else { //指令传入RequestList
RequestList.getIt().offer(request);
RequestList.getIt().notifyAll();
}
}
}
Scheduler中的锁
从RequestList中读取请求时,锁RequestList
向ElevatorCache中插入乘客时,锁ElevatorCache
synchronized (RequestList.getIt()) { //读取输入请求
if (RequestList.getIt().isNoMoreInput() &&
RequestList.getIt().isEmpty() && requests.isEmpty()) {
//结束全部Scheduler、InputHandler和Elevator
}
if (RequestList.getIt().isEmpty() && requests.isEmpty()) {
try {
RequestList.getIt().wait();
} catch (InterruptedException e) { /*nothing*/ }
}
while (!RequestList.getIt().isEmpty()) {
......
}
} //synchronized_END
if (fitElevatorC(from, destination)) {
synchronized (elevatorCaches[2]) {
elevatorCaches[2].offerPerson(request);
elevatorCaches[2].notifyAll();
}
} else if (from % 2 == 1 && destination % 2 == 1) {
synchronized (elevatorCaches[1]) {
elevatorCaches[1].offerPerson(request);
elevatorCaches[1].notifyAll();
}
} else {
synchronized (elevatorCaches[0]) {
elevatorCaches[0].offerPerson(request);
elevatorCaches[0].notifyAll();
}
}
Elevator中的锁
电梯调用ElevatorCache时,锁ElevatorCache
synchronized (cache) {
//退出程序:canInt表示Schedule已结束
if (cache.isCanInt()) {
return;
} else if (cache.isEmpty() && floor == destination
&& persons.isEmpty() && waitings.isEmpty()) {
synchronized (RequestList.getIt()) {
RequestList.getIt().notifyAll();
}
try {
cache.wait();
} catch (InterruptedException e) { /*nothing*/ }
}
}
二、调度器设置
第一次作业
调度器Manager用于读取RequestList和写EleState这两个临界区,Manager和Elevator两个进程依次运行。
第二次作业
调度器Scheduler用于读RequestList和分配请求给不同ElevatorCache这些临界区。
第三次作业
调度器Scheduler用于读RequestList和将乘客引导到不同类型的电梯的等候区ElevatorCache中。
三、第三次作业架构
第一次作业UML
第一次作业UML协作图
进程交互
Input进程
Manager进程
Elevator进程
第二次作业UML
第二次作业UML协作图
进程交互
InputHandler进程
Scheduler进程
Elevator进程
第三次作业UML
第三次作业UML协作图
进程交互
相比第二次作业,增加了换乘功能
InputHandler进程和电梯创建过程
Scheduler进程
ElevatorA(ElevatorB和ElevatorC类似)进程
第三次作业可扩展性分析
第三次作业的程序架构有较好的可扩展性。
(2)支持乘客换乘,对于不同的乘客需求,可支持不同换乘调度的扩展。
(3)可在调度器Scheduler中方便地加入不同调度策略。
四、自己程序BUG
第一次作业出现的bug是,调度器在一种特殊的情况下会发生电梯超员的情况,原因是Scheduler中调度算法中一个循环的判断条件没有判断电梯内人数情况。课下测试程度不够,没有覆盖到可能会出现问题的情况。
第三次作业出现的bug是电梯等待列表中有乘客,但电梯却保持不运行,但线程没有进入休眠,导致轮询。轮询产生的位置在ElevatorA.run()中。导致该问题的代码,在Elevator.getOn(),即电梯上乘客的方法中。我设置了一个waitings的List和一个persons的List,分别表示等待上电梯的乘客和已经在电梯中的乘客。这个问题的原因在于,当电梯内乘客persons为空时,但等待电梯的乘客waitings不为空时,电梯没有运行去接waitings中的乘客,且由于waitings不为空,电梯线程不进入休眠,导致轮询产生。解决方法是增加一个对该种情况的处理流程。
五、寻找程序BUG
首先通过自动评测机找到错误数据点,再基于该数据点进行手动测试。通过删减和增加数据内容,定位程序bug产生原因。该策略能够找到一些随机测试能覆盖到的bug,且比较有效。对于一些进程安全问题,也能够有效的发现,特别是死锁问题。
另外,我还手动构造一些临界数据,去测试程序的“抗压”能力。但并没有成功hack到。
本单元的测试策略和第一单元不尽相同。主要在于多线程的测试,有时候需要多次测试,才能确定由线程安全产生的bug是否存在。而第一单元每次程序执行的结果都是确定的,不需要反复测试同一数据点。
六、心得体会
在本次作业中,我对于线程知识有了一定的了解,能够根据需求去较为合理的划分线程,并且组织线程间的交互。在此设计过程中,线程安全问题是关键。若把线程间的临界区设置过小,可能不能保证线程的安全性;而把临界区设置过大,一定会导致程序的效率低下。我的解决方法是把临界区设为一个class实例,在class中尽量精简,这样就能兼顾安全和效率,比较有效。
在这次作业中,层次化设计依然十分重要。层次化设计贯穿了第一单元和第二单元作业,层次化设计的不足会影响程序的实现难度,并且也大概率会影响到下一次作业的迭代过程。通过本单元的学习,我进一步加深了对层次化设计的理解,相比第一单元已经能更合理地划分层次和实现需求。但我仍然需要进一步加深学习,尽可能做到更好。
此外,在本单元出现的bug较多,这令我意识到测试的重要性。所谓二八分原则,二成的时间用于实现代码,八成的时间用于防错,不是没有道理的o(╥﹏╥)o。
posted on
浙公网安备 33010602011771号