其他分享
首页 > 其他分享> > OO第二单元-多线程

OO第二单元-多线程

作者:互联网

考虑到三次作业的迭代性,我将详细的文件结构度量分析都放在了task3的部分里,前两次就简单略过了。

task1

初识多线程时的个人思考

线程涉及


提醒自己:为什么有的方法/代码块必须要加synchronized

同步(即带有synchronized的)代码块/方法,其需要设置为同步,是因为,该方法/代码块所操作的数据有可能被不同的线程在同时操作。

如果不设置为同步,那就:

想清楚这个,我们就可以:

而非盲目的随意添加。


性能优化点

注意点

UML类图

image-20220502161208719

度量分析

image-20220502141139112

bug分析

bug

本次强测、互测错了好几个点,都是因为电梯超速。我一开始百思不得其解,后来,注意到是在电梯开关门的时候有bug:

电梯在某层停下来时,我的实现方案是:

// ElevatorController类的方法
private void halt() {
    Debug.println("halt!");
    new Thread(() -> {
        open();
        close();
    }).start();

    // 先出
    ...
    // 后进
    ...

    synchronized (this) {
        if (isPassable()) {
            try {
                Debug.println("正在等待着关门..");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

就是说,在halt()函数中:

其中,关门的函数如下:

// ElevatorController类的方法
private void close() {
    elevator.close();
    setPassable(false);
    MyOutput.println(String.format(
        "CLOSE-%c-%d-%d", getBlock().getBlockId(), curFloor, elevator.getId()));
    Debug.println("电梯门close时,更新状态:");
    updateState();
}
// ElevatorController类的方法
private synchronized void setPassable(boolean passable) {
    this.passable = passable;
    notifyAll();
}

bug就出在这里:

事实上,应该先输出CLOSE信息,再进行上下移动,才是正确的。

bug修复

只需将setPassable()放在输出语句的后面。确保先输出CLOSE信息,再setPassable(),然后才notify,然后halt()才结束、开始接下来的新一轮run()

// ElevatorController类的方法
private void close() {
    elevator.close();
    MyOutput.println(String.format(
        "CLOSE-%c-%d-%d", getBlock().getBlockId(), curFloor, elevator.getId()));
    setPassable(false);
    Debug.println("电梯门close时,更新状态:");
    updateState();
}

task2

UML类图

image-20220502161140530

度量分析

image-20220502141725043

bug分析

bug

hack point:

[1.1]ADD-floor-512-1
[4.0]63-FROM-E-1-TO-D-1
[4.0]391-FROM-C-1-TO-A-1
[4.2]101-FROM-A-1-TO-B-1
[4.2]118-FROM-B-1-TO-E-1

我的部分错误输出:

[   4.2490]ARRIVE-B-1-512
[   4.4500]ARRIVE-C-1-512
[   4.6510]ARRIVE-D-1-512
[   4.8510]ARRIVE-E-1-512
[   5.0520]ARRIVE-A-1-512
[   5.0530]OPEN-A-1-512
[   5.0570]IN-101-A-1-512
[   5.4540]CLOSE-A-1-512
[   5.6540]ARRIVE-B-1-512
[   5.6550]OPEN-B-1-512
[   5.6560]OUT-101-B-1-512
[   6.0560]CLOSE-B-1-512
[   6.2560]ARRIVE-C-1-512
[   6.4570]ARRIVE-D-1-512
[   6.6580]ARRIVE-E-1-512
[   6.8580]ARRIVE-A-1-512
[   7.0580]ARRIVE-B-1-512
[   7.2590]ARRIVE-C-1-512
[   7.4600]ARRIVE-D-1-512
...

产生的问题

这样,电梯就会陷入一直右行的状态,但是想要左行的在等电梯的人无法进入电梯,程序陷入死循环,118、391、63号Person根本无法进入电梯,最终TLE

究其原因,是因为,我在编写横向电梯的代码时,Person对象进入电梯的标准沿用了和纵向电梯一样的标准,为:电梯方向和请求方向一致才能进入电梯。没有考虑到横向电梯和纵向电梯还有一个很大的差别在于:

bug修复

这回,我的程序输出:

[   4.2430]ARRIVE-E-1-512
[   4.2440]OPEN-E-1-512
[   4.2490]IN-63-E-1-512
[   4.6450]CLOSE-E-1-512
[   4.8450]ARRIVE-D-1-512
[   4.8450]OPEN-D-1-512
[   4.8460]OUT-63-D-1-512
[   5.2460]CLOSE-D-1-512
[   5.4460]ARRIVE-C-1-512
[   5.4470]OPEN-C-1-512
[   5.4470]IN-391-C-1-512
[   5.8470]CLOSE-C-1-512
[   6.0470]ARRIVE-B-1-512
[   6.0480]OPEN-B-1-512
[   6.0480]IN-118-B-1-512
[   6.4480]CLOSE-B-1-512
[   6.6490]ARRIVE-A-1-512
[   6.6490]OPEN-A-1-512
[   6.6490]OUT-391-A-1-512
[   6.6500]IN-101-A-1-512
[   7.0500]CLOSE-A-1-512
[   7.2510]ARRIVE-E-1-512
[   7.2510]OPEN-E-1-512
[   7.2520]OUT-118-E-1-512
[   7.6520]CLOSE-E-1-512
[   7.8520]ARRIVE-A-1-512
[   8.0520]ARRIVE-B-1-512
[   8.0530]OPEN-B-1-512
[   8.0530]OUT-101-B-1-512
[   8.4530]CLOSE-B-1-512

task3

UML类图

image-20220502161022540

我的换乘策略

        如图所示,有个人想p0->p3.那就需要先p0->p1
        ____________________________________
        |      |      |      |      |      |
        |      |      |______|      |      |
        |      |      |__p3__|      |      |
        |      |      |      |      |      |
        |______|      |______|      |      |
        |__p1__|      |__p2__|      |      |   // 中转层:hecTsf所在层数(若hecTsf为null,则中转层为1)
        |      |      |      |      |      |
        |______|      |      |      |      |
        |__p01_|      |      |      |      |
        |      |      |      |      |      |
        |______|      |      |      |      |
        |__p0__|      |      |      |      |
        |      |      |      |      |      |
        |______|______|______|______|______|
    
        如果hecTsf不为null,则p1应该尽可能接近p3所在层(尽量一步到位,避免出现p0->p01之后,新的PersonRequest又进行p01->p1)

文件结构

image-20220502150234475

度量分析

Class Metrics

image-20220502145116876

Main类里的圈平均复杂度有些高。我认为原因是:我直接在Main.main()方法里通过for循环来实现了加入电梯。

MyInput类里的圈平均复杂度有些高。我认为原因是:出现了较多的if-else。这也无可非议,因为该类和Scheduler类的instance直接关联,只有通过条件判断才能将不同类型的指令(PersonRequest、横向ElevatorRequest、纵向ElevatorRequest)交给Scheduler的instance。

ElevatorController和HorElevatorController(两者相似,下面仅展示ElevatorController的类内方法)的类总圈复杂度有些高。我认为原因是:

Method Metrics

image-20220502145318160
image-20220502145409848

其中,ElevatorController和HorElevatorController的updateState()的iv(G)和v(G)和CogC较高。我认为原因是:

此外,ElevatorController和HorElevatorController的run()的CogC和ev(G)较高。我认为原因是:

bug分析

有一个强测点RTLE了。我猜测应该是自己的换乘思路比较简单。

我在本地测试了很多遍,都是比时间限制(155s)要短十秒以上。于是在bug修复时,我尝试将原版重新提交了一遍,又全部ac了。而且这次比时间限早了15秒左右。可见多线程的在竞争分配上的一些不同(比如第一次执行时A电梯抢到了a人,第二次执行时B电梯抢到了a人),就会导致很显著的差别。

总结

这一单元,相比多项式解析单元,代码量和复杂度会有所下降。但是,多线程本身带来的诸多问题却非常值得我们重视。

总之,这一单元的“玄学”bug比上一单元多了很多,究其原因就是多线程引发的CPU在调度、分配时间片上的问题。让我感叹有趣的同时,也从一个全新的视角领悟到:原来软件、程序和我们的硬件、操作系统的关联,是如此紧密。

标签:OO,多线程,512,电梯,线程,CLOSE,ARRIVE,bug,单元
来源: https://www.cnblogs.com/wlc000/p/16216052.html