Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
As stated earlier, one of the motivations for using
threading is to create a responsive user interface. Although we don t cover graphical
user interfaces in this book, you can still see a simple example of a console-based
user interface.
The following example reads lines from a file and prints
them to the console, sleeping (suspending the current thread) for a
second after each line is displayed. (You ll learn more about sleeping later in
the chapter.) During this process, the program doesn t look for user input, so
the UI is unresponsive:
//: C11:UnresponsiveUI.cpp {RunByHand}
// Lack of threading produces an unresponsive UI.
//{L} ZThread
#include <iostream>
#include <fstream>
#include <string>
#include "zthread/Thread.h"
using namespace std;
using namespace ZThread;
int main() {
cout << "Press <Enter> to
quit:" << endl;
ifstream file("UnresponsiveUI.cpp");
string line;
while(getline(file, line)) {
cout << line << endl;
Thread::sleep(1000); // Time in milliseconds
}
// Read input from the console
cin.get();
cout << "Shutting down..." <<
endl;
} ///:~
To make the program responsive, you can execute a task that
displays the file in a separate thread. The main thread can then watch for user
input so the program becomes responsive:
//: C11:ResponsiveUI.cpp {RunByHand}
// Threading for a responsive user interface.
//{L} ZThread
#include <iostream>
#include <fstream>
#include <string>
#include "zthread/Thread.h"
using namespace ZThread;
using namespace std;
class DisplayTask : public Runnable {
ifstream in;
string line;
bool quitFlag;
public:
DisplayTask(const string& file) : quitFlag(false)
{
in.open(file.c_str());
}
~DisplayTask() { in.close(); }
void run() {
while(getline(in, line) && !quitFlag) {
cout << line << endl;
Thread::sleep(1000);
}
}
void quit() { quitFlag = true; }
};
int main() {
try {
cout << "Press <Enter> to
quit:" << endl;
DisplayTask* dt = new
DisplayTask("ResponsiveUI.cpp");
Thread t(dt);
cin.get();
dt->quit();
} catch(Synchronization_Exception& e) {
cerr << e.what() << endl;
}
cout << "Shutting down..." <<
endl;
} ///:~
Now the main( ) thread can respond immediately
when you press <Return> and call quit( ) on the DisplayTask.
This example also shows the need for communication between
tasks the task in the main( ) thread needs to tell the DisplayTask
to shut down. Since we have a pointer to the DisplayTask, you might
think of just calling delete on that pointer to kill the task, but this
produces unreliable programs. The problem is that the task could be in the
middle of something important when you destroy it, and so you are likely to put
the program in an unstable state. Here, the task itself decides when it s safe
to shut down. The easiest way to do this is by simply notifying the task that
you d like it to stop by setting a Boolean flag. When the task gets to a stable
point it can check that flag and do whatever is necessary to clean up before
returning from run( ). When the task returns from run( ),
the Thread knows that the task has completed.
Although this program is simple enough that it should not
have any problems, there are some small flaws regarding inter-task
communication. This is an important topic that will be covered later in this
chapter.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |