zoukankan      html  css  js  c++  java
  • 软件构造-经验-重绘,GUI与多线程的一次debug

    记一次debug

    在哈工大软件构造的lab6中,要求我们用多线程来对猴子过河的决策过程进行仿真。

    这个实验的构造思路其实比较简单,就是为每个猴子创建一个决策线程,每一只猴子都有自己的决策方式,但是所有猴子共用一条河。这也就意味着race condition的存在。

    可能出现这样的情况,两只猴子同时决策选择同一个位置,结果会撞在一起,轻则位置重叠,重则程序崩溃。

    为了避免这种情况,就需要我们小心翼翼地规划,保证线程安全的同时又要尽量提升效率。

    学会多线程安全编程就是这次实验的主要目的。

    实验中只要求实现GUI,没有要求实现动画效果,但是由于我比较闲,就是想试试看,于是我决定尝试动画化猴子的过河过程。

    初步实现动画效果

    我的设想是采用JComponent来进行猴子过河的绘制。

    实验指导书上的要求说了,所有的猴子都是以1s为单位进行决策,这也就是说,所有猴子每1s都会更新一次状态,即使猴子并没有动。

    如果要实现完全的动画效果,也就是把每一只猴子的移动过程都展现出来的话,假如有30只猴子,就意味着1s要进行30次重绘,猴子数量一多,势必影响到正常的决策计算的判断,会使猴子的运动仿真不准确,因此这是不可取的。

    所以我觉得每秒种,在计时器驱动所有猴子进行决策之前,调用一次重绘,这样就能将资源消耗最小化。

    基本的编程思路是这样的。

    维护ladder上猴子位置的数据结构是一个Map<Integer, Integer> 的List,map的key值是猴子在ladder上的坐标,value就是猴子的id。list的不同元素代表不同的ladder

    每秒钟的开始,用一个VisualMonkey类对该list进行读取,然后将其转化为坐标值,再传给Component,用以在Component上绘制。

    这样设计的初衷是为了以后可以容易扩展一些,尽管把这个实验交上去之后我肯定不会想回来再看这个代码一眼。

    基本思路很简单。

    写好了程序之后尝试运行一下

    。。。。。。咦?为什么重叠了。

    出现了很严重的问题,同一只猴子同时出现在了ladder上的多个位置,一定是哪里出错了

    排查bug

    componnet的问题?

    我看到这个bug的第一反应是——component重绘失败了,只绘制了新的图案,没有擦出旧的图案。

    后面的事实证明这个猜测不无道理,但是我当时信誓旦旦错误一定出在这里,的确耽误了不少时间。

    我在网上搜索了很多关于java的组件重绘的知识,包括repaint,validate,revalidate,update都试过了,从component到外面包含的Panel,遍历调用一次所有与重绘有关的方法,都解决不了这个问题。

    接着我尝试了在Component的paintComponent方法里面对g进行清空,然后重新绘制,失败

    接着我尝试重新创建Component对象,一切从头开始,但依旧失败了,

    按照正常的逻辑,component部分应该没有问题,bug出在别的地方了,但是由于Lab3种与swing有着极大的怨气,所以我无论如何都想把它打倒,就这样耗费了一个小时,代码已经魔改到我也看不懂了,我终于决定探索别的道路。

    被遗忘的update

    我开始怀疑,也许component的重绘被忠实的执行了,有一种可能就是,旧的猴子位置没有被删除。

    由于对猴子的ADT进行测试的时候,它们完美地完成了使命,所以我仍然觉得这里不会出问题。

    等等,我好像忘记了什么

    ???

    ADT没有错,component也没有错,那么问题出在......?

    ......

     啊,原来如此。

    通过断点找到了VisualMonkey类,发现果然在从List到坐标值的换算种出了差错,我只对map进行了put,却没有把以前的clear

    这不是线程安全带来的,但是多线程的复杂性的确会让人容易忽视这些问题。

    看来需要在每次转换之前,把map清空

    image的特殊性

    让我们跑一次猴子看看

    。。。。。。??

    怎么还是有重叠的猴子?

    仔细观察,发现前面的猴子都没有名字,只有一个猴子的图像,我大概知道了问题所在。

    为了追求动画效果,我是使用emoji图片来表示猴子的。由于Image对象不属于Graphic对象

    说的就是这个参数g

    在paintComponent里面,我都是直接调用add方法来添加图片的

     而那些线条之类的元素是添加到Graphics对象里的:

    调用repaint的时候,Graphics对象里的线条都被清空了,但是图片并没有被删除。

    为了解决这个问题,我设置了一个set,每次把图片加到component上面的时候,同时把它们添加进这个set里

    然后在每次repaint的时候,都要将component上的所有图片清空一次

    现在应该没有问题了吧?

    跑一下猴子看看:

    终于解决了。

    感想

    在debug的过程中,应当冷静分析

    有时候要认真思考问题出现的地方,比如说我执迷于打倒component,就忽视了别的地方。

  • 相关阅读:
    OCP-1Z0-051-V9.02-55题
    OCP-1Z0-051-V9.02-60题
    OCP-1Z0-053-V12.02-59题
    OCP-1Z0-053-V12.02-184题
    OCP-1Z0-053-V12.02-595题
    OCP-1Z0-053-V12.02-584题
    OCP-1Z0-053-V12.02-234题
    OCP-1Z0-053-V12.02-548题
    OCP-1Z0-053-V12.02-549题
    OCP-1Z0-053-V12.02-551题
  • 原文地址:https://www.cnblogs.com/giere/p/10961144.html
Copyright © 2011-2022 走看看