Concurrency |
We introduced active objects early in the course as a way of providing objects which behaved independently of user actions. Examples include vehicles and lanes from Frogger, the ball in BoxBall, and the falling snow class example. Our ActiveObject class encompasses what are usually termed threads of control. The Frogger game, for instance, included separate threads for each vehicle, each lane, and the part of the system that listened for user clicks and moved the frog appropriately.
Today, we will discuss complications that arise with threads and
provide some indications of how to handle them. As a first example we
look at a program, BetterBubble, which uses a graphic user
interface in order to simulate a bubble pipe. When the user pushes
the "start blowing" button, a bubble starts growing at the top of
the bowl of the pipe. When the "release blowing" button is pushed,
the bubble stops blowing and floats off of the top of the screen.
BetterBubble
demo
If we did not use an ActiveObject for this program, it would
not work properly. That is because the process that handles the
button presses is also responsible for drawing the pictures on the
screen. If that process is busy moving the bubble it will not have
the opportunity to listen to button presses or to redraw pictures on
the screen.
The standard way to ensure prompt processing of user interface events is to make sure that the thread handling user events is never tied up with long, complex actions. Instead, we create a new ActiveObject to handle the complex task, so that the user thread can stay alert to new events.
The begin() method draws the bubble pipe and creates and installs the buttons. When the start button is pressed, the constructor of BubbleBlower is called. This saves the canvas in an instance variable and starts the new thread. The event-handling thread then returns, waiting for another user event.
When the release button is pressed, it sends the stopBlowingBubble method to blower. Thus the event-handling thread sets the stopBlowing variable of blower to be true. It then returns and waits for another user action.
Meanwhile, the blower's own thread is busy executing the while loop in blowBubble. However, when it now finishes execution of the body of the while loop, it will notice that the value of stopBlowing has changed, and it will exit the loop. Thus the execution of method blowBubble will terminate, returning control to the run method. However the next statement after the call of blowBubble in the body of run is the method releaseBubble. Thus it will start floating the bubble. When the bubble has floated off the screen, the releaseBubble and run methods will terminate and the active object will die.
Each press of the start button will result in the initiation of a new active object to handle the blowing and release of a new bubble. Note however, that all is not entirely well. If the user presses start twice in a row without hitting release between the two, the first bubble can no longer be affected by clicking on release. See if you can figure out why this happens, and how we might fix it in some way.
Concurrency |