Cross-Platform GUI Programming with wxWidgets

Using wxThread

If you want to implement functionality using threads, you write a class that derives from wxThread and implements at least the virtual EnTRy method, which is where the work of the thread takes place. Let's say you wanted to use a separate thread to count the number of colors in an image. Here's the declaration of the thread class:

class MyThread : public wxThread { public: MyThread(wxImage* image, int* count): m_image(image), m_count(count) {} virtual void *Entry(); private: wxImage* m_image; int* m_count; }; // An identifier to notify the application when the // work is done #define ID_COUNTED_COLORS 100

The Entry function does the calculation and returns an exit code that will be returned by Wait (for joinable threads only). Here's the implementation for EnTRy:

void *MyThread::Entry() { (* m_count) = m_image->CountColours(); // Use an existing event to notify the application // when the count is done wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_COUNTED_COLORS); wxGetApp().AddPendingEvent(event); return NULL; }

For simplicity, we're using an existing event class to send a notification to the application when the color count is done.

Creation

Threads are created in two steps. First the object is instantiated, and then the Create method is called:

MyThread *thread = new MyThread(); if ( thread->Create() != wxTHREAD_NO_ERROR ) { wxLogError(wxT("Can't create thread!")); }

There are two different types of threads: the ones that you start and then forget about and the ones from which you are awaiting a result. The former are called detached threads, and the latter are joinable threads. Thread type is indicated by passing wxTHREAD_DETACHED (the default) or wxThrEAD_JOINABLE to the constructor of a wxTHRead. The result of a joinable thread is returned by the Wait function. You cannot wait for a detached thread.

You shouldn't necessarily create all threads as joinable, however, because joinable threads have a disadvantage; you must wait for the thread using wxThread::Wait or else the system resources that it uses will never be freed, and you must delete the corresponding wxTHRead object yourself (once used, it cannot be reused). In contrast, detached threads are of the "fire-and-forget" kind. You only have to start a detached thread, and it will terminate and destroy itself.

This means, of course, that all detached threads must be created on the heap because the thread will call delete this upon termination. Joinable threads may be created on the stack, although usually they will be created on the heap as well. Don't create global thread objects because they allocate memory in their constructor, which will cause problems for the memory checking system.

Specifying Stack Size

You can indicate the desired stack size for the thread as a parameter for Create. Passing zero tells wxWidgets to use the platform default.

Specifying Priority

Some operating systems let the application supply a hint about how much execution time a thread needs. Call wxThread::SetPriority with a number between 0 and 100, where 0 is the minimum and 100 is the maximum. The symbols WXThrEAD_MIN_PRIORITY, wxThrEAD_DEFAULT_PRIORITY, and wxThrEAD_MAX_ PRIORITY are predefined, with values 0, 50, and 100, respectively. You should call SetPriority after calling Create but before calling Run.

Starting the Thread

After calling Create, the thread is not yet running. You need to call wxThread:: Run to start the thread, and wxWidgets will call the thread's Entry function.

How to Pause a Thread or Wait for an External Condition

If a thread needs to wait for something to happen, you should avoid both polling and idling in a loop that keeps the processor busy doing nothing ("busy waiting").

If you just want to wait a few seconds, then send the thread to sleep using wxThread::Sleep.

If you are waiting for something else to happen, you should use a call that blocks execution of the thread until you are notified of a change. For example, if you are using sockets in a thread, you should use blocking socket calls, which will simply pause or "hang" until data is available so that no cycles are wasted. Or if you are waiting for data in a queue and are using a joinable thread, you should block on the Wait method.

You might be tempted to use the Pause and Resume functions to temporarily put your thread to sleep. However, there are a couple of problems with this approach. First, because Pause may be emulated on some operating systems (notably POSIX systems), the thread must periodically call TestDestroy and terminate as soon as possible if it returns true. Secondly, it is very difficult to get right. An operating system may suspend you at any moment, which could easily lead to an application deadlock because you might lock a mutex at that moment.

So in most cases, it is not really a sound design to use Pause and Resume. You should try to transform your code to wait for synchronization objects (see the following section, "Synchronization Objects").

Termination

As we have mentioned, detached threads are automatically destroyed after completion. For a joinable thread, you can simply call wxTHRead::Wait immediately, or in a GUI application, you can poll wxThread::IsAlive from an idle handler in the main thread and only call Wait if IsAlive returns false. Calling Wait permits thread resources to be freed. Of course, a neater alternative is to simply use a detached thread and post an event when it's done.

You can use wxTHRead::Delete to request that a thread be deleted. For this to work, the thread must periodically call TestDestroy.

    Категории