猿问

如何使用SwingWorker模拟缓冲的外围设备?

我将本练习用作教学工具,以帮助我掌握一些Java GUI编程概念。我要寻找的是一种一般的理解,而不是针对一个特定问题的详细解决方案。我希望编码这种“正确的”方法将教给我很多有关如何解决将来的多线程问题的知识。如果对于这个论坛来说太笼统了,那么它可能属于Programmers吗?


我在模拟读卡器。它具有GUI,可让我们将卡装载到料斗中并按Start(开始),依此类推,但它的主要“客户端”是CPU,在单独的线程上运行并请求卡。


读卡器维护一个缓冲区。如果有卡请求进入并且缓冲区为空,则读卡器必须从料斗读取卡(这需要1/4秒,即1962)。在将卡读入缓冲区后,读卡器将缓冲区发送到CPU,并在下一个请求之前立即启动另一个缓冲区加载操作。


如果不仅缓冲区是空的,而且料斗中没有卡,那么我们必须等到操作员将一个平台放在料斗中并按下启动键(这始终会启动缓冲区加载操作)。


在我的实现中,卡请求以invokeLater() Runnables在EDT上排队的形式发送到读卡器。在myRunnable.run()时间,无论是一个缓冲器将可用(在这种情况下,我们可以将其发送到CPU和开球另一个缓冲器负载操作),或缓冲区将是空的。如果是空的怎么办?


两种可能性:(a)飞行中已经进行了缓冲加载操作,或者(b)储卡器为空(或尚未启动)。无论哪种情况,都不能让EDT等待。工作(和等待)必须在后台线程上完成。


为了简单起见,我尝试生成一个SwingWorker来响应每个卡请求,而不管缓冲区的状态如何。伪代码为:


SwingWorker worker = new SwingWorker<Void, Void>() {

    public Void doInBackground() throws Exception {

        if (buffer.isEmpty()) {

            /*

             * fill() takes 1/4 second (simulated by Thread.sleep)

             * or possibly minutes if we need to have another 

             * card deck mounted by operator.

             */

            buffer.fill();

        }

        Card card = buffer.get(); // empties buffer

        /*

         * Send card to CPU

         */

        CPU.sendMessage(card); // <== (A) put card in msg queue

        /* 

         * Possible race window here!!

         */

        buffer.fill(); //         <== (B) pre-fetch next card

        return null;

    }

};

worker.execute();

这产生了一些奇怪的计时效果-我怀疑是buffer.fill()由于可能发生如下情况的比赛:如果在(A)和(B)之间,CPU收到了卡,发送了另一个请求,并有另一个SwingWorker线程代表它生成,则可能有两个线程同时尝试填充缓冲区。[删除(B)处的预取调用解决了该问题。]


所以我认为每次读取都产生一个SwingWorker线程是错误的。卡的缓冲和发送必须在单个线程中序列化。该线程必须尝试预取缓冲区,并且如果我们的卡用完了并且必须等待将更多的内存放入料斗中,则必须能够等待并恢复。我怀疑SwingWorker拥有长时间运行的后台线程才能处理此问题,但我还没有到位。


假设要使用SwingWorker线程,我应该如何实现这一点,消除EDT的延迟,允许该线程阻塞等待装满料斗并处理在另一个卡请求到达之前或之后是否完成缓冲区填充的不确定性?


蓝山帝景
浏览 413回答 3
3回答

慕容3067478

了解内部SwingWorker使用可能会有所帮助ExecutorService;为方便起见,它添加了临时EDT处理机制。只要您在EDT上更新GUI并同步对任何共享数据的访问,则后者等效于前者。假设您使用的是此处建议的“ 模型–视图–控制器”模式,则模型是CPU的操作。尽管可能是不同的类,但我看不出有任何理由在不同的线程上对读卡器进行建模。相反,让处理器模型具有读卡器模型,该模型在线程上等待,并在计时器触发时更新模型。让更新的模型在将事件发布到EDT的正常过程中通知视图。让控制器根据查看手势取消和安排读卡器模型。java.util.Timer

繁星coding

我附加到原始问题的“答案”中缺少一件事:我正在Thread.sleep()通过单线程执行程序将耗时的工作(仅用于教学目的)交给后台线程。但是,出现了一个问题,因为后台线程正在通过轮询(用作一个Swing组件的数据模型的)List来“读取卡”,并引发了许多AWT数组索引超出范围的异常。经过几次徒劳的尝试使EDT和我的后台线程同步对列表的访问之后,我进行了调试,并将命令轮询(包装)到List(列表)并在一个小的Runnable()中更新了GUI,并使用invokeAndWait()导致当我的后台任务等待时,它们将在EDT上运行。这是我修改后的解决方案:private ExecutorService executorService;&nbsp;:executorService = Executors.newSingleThreadExecutor();&nbsp;:/*&nbsp;* When EDT receives a request for a card it calls readCard(),&nbsp;* which queues the work to the *single* thread.&nbsp;*/public void readCard() throws Exception {&nbsp; &nbsp; executorService.execute(new Runnable() {&nbsp; &nbsp; &nbsp; &nbsp; public void run() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (buffer.isEmpty()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* fill() takes 1/4 second (simulated by Thread.sleep)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buffer.fill();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Card card = buffer.get(); // empties buffer&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Send card to CPU&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CPU.sendMessage(card); // <== (A) put card in msg queue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* No race!&nbsp; Next request will run on same thread, after us.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buffer.fill(); //&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<== (B) pre-fetch next card&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; });}/*&nbsp;* IMPORTANT MODIFICATION HERE - - -&nbsp;*&nbsp;* buffer fill() method has to remove item from the list that is the&nbsp;* model behind a JList - only safe way is to do that on EDT!&nbsp;*/private void fill() {&nbsp; &nbsp; SwingUtilities.invokeAndWait(new Runnable() {&nbsp; &nbsp; &nbsp; &nbsp; /*&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Running here on the EDT&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; &nbsp; &nbsp; public void run() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* Hopper not empty, so we will be able to read a card.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buffer = readHopper.pollLast();&nbsp; // read next card from current deck&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fireIntervalRemoved(this, readHopper.size(), readHopper.size());&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gui.viewBottomOfHopper(); // scroll read hopper view correctly&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; });&nbsp; &nbsp; // back to my worker thread, to do 1/4 sec. of heavy number crunching ;)&nbsp; &nbsp; // while leaving the GUI responsive&nbsp;&nbsp; &nbsp; Thread.sleep(250);&nbsp; &nbsp; &nbsp;:&nbsp; &nbsp; etc.}
随时随地看视频慕课网APP

相关分类

Java
我要回答