if(--countDown == 0) return;
Thread::yield();
}
}
};
int main() {
try {
ThreadedExecutor executor;
for(int i = 0; i < 5; i++)
executor.execute(new YieldingTask(i));
} catch(Synchronization_Exception& e) {
cerr << e.what() << endl;
}
} ///:~
You can see that the task’s run( ) member function consists entirely of an infinite loop. By using yield( ), the output is evened up quite a bit over that without yielding. Try commenting out the call to Thread::yield( ) to see the difference. In general, however, yield( ) is useful only in rare situations, and you can’t rely on it to do any serious tuning of your application.
Sleeping
Another way you can control the behavior of your threads is by calling sleep( ) to cease execution of that thread for a given number of milliseconds. In the preceding example, if you replace the call to yield( ) with a call to sleep( ), you get the following:
//: C11:SleepingTask.cpp
// Calling sleep() to pause for awhile.
//{L} ZThread
#include
#include "zthread/Thread.h"
#include "zthread/ThreadedExecutor.h"
using namespace ZThread;
using namespace std;
class SleepingTask : public Runnable {
int countDown;
int id;
public:
SleepingTask(int ident = 0) : countDown(5), id(ident) {}
~SleepingTask() {
cout << id << " completed" << endl;
}
friend ostream&
operator<<(ostream& os, const SleepingTask& st) {
return os << "#" << st.id << ": " << st.countDown;
}
void run() {
while(true) {
try {
cout << *this << endl;
if(--countDown == 0) return;
Thread::sleep(100);
} catch(Interrupted_Exception& e) {
cerr << e.what() << endl;
}
}
}
};
int main() {
try {
ThreadedExecutor executor;
for(int i = 0; i < 5; i++)
executor.execute(new SleepingTask(i));
} catch(Synchronization_Exception& e) {
cerr << e.what() << endl;
}
} ///:~
Thread::sleep( ) can throw an Interrupted_Exception (you’ll learn about interrupts later), and you can see that this is caught in run( ). But the task is created and executed inside a try block in main( ) that catches Synchronization_Exception (the base class for all ZThread exceptions), so wouldn’t it be possible to just ignore the exception in run( ) and assume that it will propagate to the handler in main( )? This won’t work because exceptions won’t propagate across threads back to main( ). Thus, you must handle any exceptions locally that may arise within a task.
You’ll notice that the threads tend to run in any order, which means that sleep( ) is also not a way for you to control the order of thread execution. It just stops the execution of the thread for awhile. The only guarantee that you have is that the thread will sleep at least 100 milliseconds (in this example), but it may take longer before the thread resumes execution, because the thread scheduler still has to get back to it after the sleep interval expires.
If you must control the order of execution of threads, your best bet is to use synchronization controls (described later) or, in some cases, not to use threads at all, but instead to write your own cooperative routines that hand control to each other in a specified order.
Priority
The
Here’s MoreBasicThreads.cpp modified so that the priority levels are demonstrated. The priorities are adjusting by using Thread’s setPriority( ) function.
//: C11:SimplePriorities.cpp
// Shows the use of thread priorities.
//{L} ZThread
#include "zthread/Thread.h"
#include
#include
using namespace ZThread;
using namespace std;
class SimplePriorities : public Runnable {
int countDown;
volatile double d; // No optimization
int id;
public:
SimplePriorities(int ident = 0): countDown(5),id(ident){}
~SimplePriorities() throw() {
cout << id << " completed" << endl;
}
friend ostream&
operator<<(ostream& os, const SimplePriorities& sp) {
return os << "#" << sp.id << " priority: "
<< Thread().getPriority()
<< " count: "<< sp.countDown;
}
void run() {
while(true) {
// An expensive, interruptable operation:
for(int i = 1; i < 100000; i++)
d = d + (M_PI + M_E) / (double)i;
cout << *this << endl;
if(--countDown == 0) return;
}
}
};
int main() {
try {
Thread high(new SimplePriorities);
high.setPriority(High);
for(int i = 0; i < 5; i++) {
Thread low(new SimplePriorities(i));
low.setPriority(Low);
}
} catch(Synchronization_Exception& e) {
cerr << e.what() << endl;
}
} ///:~
Here, operator<<( ) is overridden to display the identifier, priority, and countDown value of the task.