[previous] [up] [next]     [contents] [index]
Next: Defining a Class Up: MrEd Applications: An Example Previous: Handling Events

Using Threads

Our phone book interface triggers a phone book search every time the user types a character into the ``Name'' text field. This algorithm works if the phone book search is fast, but if a single search takes a relatively long time, then the user will be forced to wait after each character is typed.

This problem can be solved without changing the graphical interface by performing the phone book search in a separate thread. The procedure refresh-number-info should start a new thread each time it is called, returning immediately to process more user events. If a new search is started before the old one has completed, then refresh-number-info must send a cancellation signal to the old thread so that it does not try to display its search results. The threaded refresh-number-info is shown in Figure ``Thread''.

  (define refresh-number-info
    (let ([adj-cancel-sema (make-semaphore 1)]
          [previous-cancel (box #f)])
      (lambda ()
        (let ([this-cancel (box #f)])
          (semaphore-wait adj-cancel-sema)
          (set-box! previous-cancel #t)
          (set! previous-cancel this-cancel)
          (semaphore-post adj-cancel-sema)
          (thread
           (lambda ()
             (send number-text set-value "(Searching...)")
             (let* ([name (send name-text get-value)]
                    [home? (zero? (send number-selector get-selection))]
                    [number (lookup-number name home?)] ; May take a while...
                    [number-string (if number
                                       number
                                       "(Unknown)")])
               (semaphore-wait adj-cancel-sema)
               (unless (unbox this-cancel)
                       (send number-text set-value number-string))
               (semaphore-post adj-cancel-sema))))))))
-- Figure: A threaded phone book search --

To send a cancellation signal to an old thread, refresh-number-info uses a mutable box. The local variable previous-cancel holds the box for communicating with the most-recently spawned thread. (Previous-cancel is initialized to a box that is not used by a thread.) When refresh-number-info is invoked, it creates a new box to be used in a new thread. It then changes the previous thread's box to keep the previous thread from displaying its results. The new box is saved into previous-cancel to be used by the next call to refresh-number-info. A semaphore is a low-level synchonization object; the semaphore adj-cancel-sema is used here to make sure that a thread does not write its value while another thread is trying to cancel that value.

After preparing a cancel box, refresh-number-info spawns a new thread to perform the phone book lookup, and then returns to handle more user events. Meanwhile, the new thread finds a number in the phone book using the old lookup algorithm. Once the thread has a result to display, it first checks to see if its box has been changed; if the cancellation box has been not changed, then the thread displays its result into number-text.


[previous] [up] [next]     [contents] [index]
Next: Defining a Class Up: MrEd Applications: An Example Previous: Handling Events

PLT