[Python编程(第4版)].(Programming.Python.4th.Edition).Mark.Lutz.文字版

(yzsuai) #1

GUIs, Threads, and Queues


In Chapter 5, we learned about threads and the queue mechanism that threads typically
use to communicate with one another. We also described the application of those ideas
to GUIs in the abstract. In Chapter 9, we specialized some of these topics to the tkinter
GUI toolkit we’re using in this book and expanded on the threaded GUI model in
general, including thread safety (or lack thereof) and the roles of queues and locks.


Now that we’ve become fully functional GUI programmers, we can finally see what
these ideas translate to in terms of code. If you skipped the related material in Chap-
ter 5 or Chapter 9, you should probably go back and take a look first; we won’t be
repeating the thread or queue background material in its entirety here.


The application to GUIs, however, is straightforward. Recall that long-running oper-
ations must generally be run in parallel threads, to avoid blocking the GUI from up-
dating itself or responding to new user requests. Long-running operations can include
time-intensive function calls, downloads from servers, blocking input/output calls, and
any task which might insert a noticeable delay. In our packing and unpacking examples
earlier in this chapter, for instance, we noted that the calls to run the actual file pro-
cessing should generally run in threads so that the main GUI thread is not blocked until
they finish.


In the general case, if a GUI waits for anything to finish, it will be completely unre-
sponsive during the wait—it can’t be resized, it can’t be minimized, and it won’t even
redraw itself if it is covered and uncovered by other windows. To avoid being blocked
this way, the GUI must run long-running tasks in parallel, usually with threads that
can share program state. That way, the main GUI thread is freed up to update the
display and respond to new user interactions while threads do other work. As we’ve
also seen, the tkinter update call can help in some contexts, but it only refreshes the
display when it can be called; threads fully parallelize long-running operations and offer
a more general solution.


However, because, as we learned in Chapter 9, only the main thread should generally
update a GUI’s display, threads you start to handle long-running tasks should not
update the display with results themselves. Rather, they should place data on a queue
(or other mechanism), to be picked up and displayed by the main GUI thread. To make
this work, the main thread typically runs a timer-based loop that periodically checks
the queue for new results to be displayed. Spawned threads produce and queue data
but know nothing about the GUI; the main GUI thread consumes and displays results
but does not generate them.


Because of its division of labor, we usually call this a producer/consumer model—task
threads produce data which the GUI thread consumes. The long-running task threads
are also sometimes called workers, because they handle the work of producing results
behind the scenes, for the GUI to present to a user. In some sense, the GUI is also a
client to worker thread servers, though that terminology is usually reserved for more


GUIs, Threads, and Queues | 635
Free download pdf