Multithreading

Traditionally, applications have a single thread of execution and perform one operation at a time. For GUI programs this can sometimes be a problem—for example, if the user invokes a long-running operation, the user interface might freeze up while the operation is taking place. There are a few solutions that can be tried to eliminate this problem.

One simple solution, particularly useful in long-running loops, is to call QAp-plication.processEvents(). This method gives the event loop the opportunity to handle any unprocessed events, such as paint events, as well as mouse and key press events. Another solution is to use zero-timeout timers. We have combined both approaches in several examples, usually when loading lots of files—for example, in Chapter 9'sText Editor's MainWindow.loadFiles() method.

A third solution is to farm the work out to another program entirely. This can be done using the Python standard library's subprocess module, or using PyQt's QProcess class. The makepyqt.pyw application supplied with the examples uses QProcess to execute PyQt's command-line tools such as pyuic4 and pyrcc4.

In some cases what we really need is a separate thread of execution within the application itself. Applications that have more than one thread of execution are said to be multithreaded.* For example, we might want to create a server that can service as many simultaneous connections as the hardware can cope with, something relatively easily done if we devote a new thread to each connection. And in some cases we might have a GUI application where we want the user to be able to start off a long-running process, and then continue interacting with the application; in such cases it may be best to pass on the processing to a separate secondary thread and leave the primary (GUI) thread free to respond to the user.

This chapter shows some common techniques used in multithreaded programming. These are enough to get started, but the coverage is not comprehensive,

* This chapter assumes a knowledge of the fundamentals of threading. For a thorough, but not light, introduction, see Foundations of Multithreaded, Parallel, and Distributed Programming.

since that would take us beyond the scope of the book and would require a book in itself.

Because several threads may access the same data concurrently, multithreaded applications are usually more difficult to write, maintain, and debug than single-threaded applications. On single-processor machines, multithreaded applications can sometimes run slower than single-threaded applications (due to the processing overhead of having the additional threads), but they are usually perceived to run faster by users because they don't freeze the user interface, and because they make it much easier for progress to be reported back to the user incrementally.

Using the right number of threads can significantly affect performance. For example, in the Page Indexer example covered later in the chapter, we have a primary (GUI) thread and a secondary thread. The exercise involves changing this example to use multiple secondary threads. If too many are used, the application runs slower than the version with one secondary thread, but with the right number, we can start up the one secondary thread version, and then start up the multiple secondary thread version, and see the multiple secondary thread version catch up, overtake, and finish, before the one secondary thread version has finished. How many secondary threads should we use? The answer depends on what processing must be done and on the particular machine and operating system that the application is run on. We could experiment with realistic datasets to fix a number, or we could make our code use more or fewer secondary threads depending on circumstances.

Python's standard library provides the low-level thread module and the higherlevel threading module, but for PyQt programming, we recommend using the PyQt threading classes. PyQt's threading classes offer a high-level API, but under the hood some of their basic operations are implemented in assembly language to make them as fast and fine-grained as possible, something not done in Python's threading modules.

PyQt applications always have at least one thread of execution, the primary (initial) thread. In addition, they may create as many secondary threads as they need. However, if the application has a GUI, the GUI operations, such as executing the event loop, may only take place in the primary thread. New threads are created by instantiating QThread subclasses that reimplement the QThread.run() method.

It is possible to create PyQt applications that do not have a GUI, using QCoreApplication instead of QApplication. Just like GUI PyQt applications, they have one primary thread and may have any number of secondary threads.

Communication between secondary threads and the primary thread is often desirable—for example, to keep the user informed of progress, to allow the user to intervene during processing, and to let the primary thread know when processing is complete. Traditionally, such communication has taken place by using shared variables in conjunction with a resource protection mechanism.

PyQt has classes to support this approach, including QMutex, QReadWriteLock, and QSemaphore. In addition, PyQt applications can use the signal-slot mechanism to communicate between threads; this is very convenient and useful.

In this chapter's first section we will look at a threaded TCP server; it does the same job as the server described in the preceding chapter's last section, but it can serve several clients simultaneously because it is threaded. In the second and third sections we will look at a GUI application that has some potentially very time-consuming processing to do, and that passes on the processing to a secondary thread. This application uses signals and slots to keep the user interface up-to-date regarding progress, and to provide the user with some control over the secondary thread. This example also uses some of the resource protection classes so that the user interface can access work in progress.

0 0

Responses

  • tesfay ambessa
    Which library has pyrcc4?
    6 years ago
  • bucca
    How to make pyqt query?
    6 years ago

Post a comment