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 (or Thread) 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 (or Thread) 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 the run method. 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 the while loop will terminate. However the next statement after the while loop 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 |