return os << "#" << a.id << ": " << a.tlv->get();
}
};
int main() {
cout << "Press
try {
CountedPtr
tlv(new ThreadLocalVariables);
const int sz = 5;
ThreadedExecutor executor;
for(int i = 0; i < sz; i++)
executor.execute(new Accessor(tlv, i));
cin.get();
tlv->cancel(); // All Accessors will quit
} catch(Synchronization_Exception& e) {
cerr << e.what() << endl;
}
} ///:~
When you create a ThreadLocal object by instantiating the template, you are only able to access the contents of the object using the get( ) and set( ) functions. The get( ) function returns a copy of the object that is associated with that thread, and set( ) inserts its argument into the object stored for that thread, returning the old object that was in storage. You can see this is use in increment( ) and get( ) in ThreadLocalVariables.
Since tlv is shared by multiple Accessor objects, it is written as Cancelable so that the Accessors can be signaled when we want to shut the system down.
When you run this program, you’ll see evidence that the individual threads are each allocated their own storage.
Terminating tasks
In previous examples, we have seen the use of a "quit flag" or the Cancelable interface in order to terminate a task. This is a reasonable approach to the problem. However, in some situations the task must be terminated more abruptly. In this section, you’ll learn about the issues and problems of such termination.
First, let’s look at an example that not only demonstrates the termination problem but is also an additional example of resource sharing. To present this example, we’ll first need to solve the problem of iostream collision
Preventing iostream collision
You may have noticed in previous examples that the output is sometimes garbled. The problem is that C++ iostreams were not created with threading in mind, and so there’s nothing to keep one thread’s output from interfering with another thread’s output.
To solve the problem, we need to create the entire output packet first and then explicitly decide when to try to send it to the console. One simple solution is to write the information to an ostringstream and then use a single object with a mutex as the point of output among all threads, to prevent more than one thread from writing at the same time:
//: C11:Display.h
// Prevents ostream collisions
#ifndef DISPLAY_H
#define DISPLAY_H
#include "zthread/Mutex.h"
#include "zthread/Guard.h"
#include
#include
class Display { // Share one of these among all threads
ZThread::Mutex iolock;
public:
void output(std::ostringstream& os) {
ZThread::Guard
std::cout << os.str();
}
};
#endif // DISPLAY_H ///:~
This way, all of the standard operator<<( ) functions are predefined for us and the object can be built in memory using familiar ostream operators. When a task wants to display output, it creates a temporary ostringstream object that it uses to build up the desired output message. When it calls output( ), the mutex prevents multiple threads from writing to this Display object. (You must use only one Display object in your program, as you’ll see in the following examples.)
This just shows the basic idea, but if necessary, you can build a more elaborate framework. For example, you could enforce the requirement that there be only one Display object in a program by making it a
The ornamental garden
In this simulation, the garden committee would like to know how many people enter the garden each day though its multiple gates. Each gate has a turnstile or some other kind of counter, and after the turnstile count is incremented, a shared count is incremented that represents the total number of people in the garden.
//: C11:OrnamentalGarden.cpp
//{L} ZThread
#include "Display.h"
#include "zthread/Thread.h"
#include "zthread/FastMutex.h"
#include "zthread/Guard.h"
#include "zthread/ThreadedExecutor.h"
#include "zthread/CountedPtr.h"
#include
#include
#include
using namespace ZThread;
using namespace std;
class Count : public Cancelable {
FastMutex lock;
int count;
bool paused, canceled;
public:
Count() : count (0), paused(false), canceled(false) {
srand(time(0)); // Seed the random number generator
}
int increment() {
// Comment the following line to see counting fail:
Guard
int temp = count ;
if(rand() < RAND_MAX/2) // Yield half the time
Thread::yield();
return (count = ++temp);
}
int value() {
Guard
return count;
}
void cancel() {
Guard
canceled = true;
}
bool isCanceled() {
Guard
return canceled;
}
bool pause() {
Guard
paused = true;
}
bool isPaused() {
Guard
return paused;
}
};
class Entrance : public Runnable {
CountedPtr
CountedPtr
int number;
int id;