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,就忽视了别的地方。

  • 相关阅读:
    Best Time to Buy and Sell Stock III
    Valid Palindrome
    Longest Substring Without Repeating Characters
    Copy List with Random Pointer
    Add Two Numbers
    Recover Binary Search Tree
    Anagrams
    ZigZag Conversion
    Merge k Sorted Lists
    Distinct Subsequences
  • 原文地址:https://www.cnblogs.com/giere/p/10961144.html
Copyright © 2011-2022 走看看