Should I be using a Java “worker thread” for this

2019-07-01 10:53发布

问题:

I'm writing an emulator of an old computer in Java/Swing, and I think I've identified the design problem that I'm having. As idiosyncratic as this application is, I suspect someone will find a "pattern" to this problem.

I should add that I'm still a beginner at OOP, GUIs, and Design Patterns.

The machine has a GUI thread (Console) - with pushbuttons and switches, and a Model thread (CPU) with which the Console communicate to cause Console events to change the CPU's state. The Console, of course, is event-driven from the AWT event queue. The Console communicates with the CPU by queueing messages on a Priority Blocking Queue, which the CPU receives. As such, the CPU is structured as an event-loop, too. So far, so good.

The problem is that, when you push START on the Console, you want the CPU to start executing whatever program it has in its memory. It still needs to respond to switch throws and button-pushes (such as STOP) from the Console, but it mainly needs to sit there and spin through its instruction fetch-decode-execute loop.

And even that wasn't problematic for a while: I had a method called Cycle() that would execute one specific "cycle" of the current instruction and then return, to be immediately redispatched to execute the next cycle. I placed the call to Cycle() inside the CPU's run loop, and polled the message queue after every cycle. If the CPU was stopped, the run loop would simply wait on the message queue.

Now: I'm implementing the I/O instructions, e.g. Read Card. It's necessary for one of the cycles to send a request for data to the peripheral device in question (itself implemented as a GUI/Model running on separate threads) and then wait for the data to arrive. This completely breaks the whole notion of the CPU being a simple event-loop that receives messages and acts upon them without incurring blocking in the process. This new cycle will block. And if the operator hasn't loaded a card deck into the reader, it can block a long time.

I thought about factoring the instruction fetch-decode-execute loop out into a separate "worker" thread, but I don't think it's a good fit, since worker threads (as I understand them) are intended to run asynchronously to completion, and do not continue interact with their parent thread while running. (In fact, I can't think of why the "worker thread" should ever terminate.) Also, there is currently no synchronization necessary when a cycle needs to access data that can concurrently be modified as a result of console key presses.

So how do I manage to meld "event-driven" processing with a traditional batch-process which needs to explicitly wait for messages before proceeding?

回答1:

Worker threads (as I understand them) are intended to run asynchronously to completion, and do not continue to interact with their parent thread while running.

A common implementation, SwingWorker, has no particular time limit, and it can communicate results on the event dispatch thread continually during it's lifetime via the publish() method. The only potential problem I see is that the corresponding process() method may receive coalesced results. For reference, here is an example.

Alternatively, have your CPU model use a timer to drive the fetch-decode-execute loop. javax.swing.Timer is convenient, as its action event handler executes on the event dispatch thread. You'll have to synchronize access to any shared data using the techniques described in this article.

Finally, you might look at this answer that mentions a 6502 emulation in Java.



回答2:

In a typical real system, each separate device would in fact be running in parallel (i.e., on its own thread), but it certainly makes sense to model it in your simulation as a separate thread. You'll want to make sure that you implement some sort of analog to the interrupt system in real CPUs to handle the synchronization when the worker has finished its job (e.g., a new priority queue to catch "I/O interrupts" and the like). You may find it helpful to pick up a copy of The Design of the UNIX Operating System by Maurice Bach. It goes into great detail on how UNIX interacts with the underlying hardware, and may be a good resource for your project.