CS11 C++ DGCcourses.cms.caltech.edu/cs11/material/dgc/lectures/... · This Week’s Lab Implement a...
Transcript of CS11 C++ DGCcourses.cms.caltech.edu/cs11/material/dgc/lectures/... · This Week’s Lab Implement a...
CS11 C++ DGC
Lecture 3Spring 2006-2007
Today’s Topics
POSIX condition variablesfriend access modifierStatic functionsThe Standard Template Library
This Week’s Lab
Implement a thread poolVery common threading component
Pool of N threadsThreads are expensive to create and destroyToo many threads can overwhelm the system
Context-switches, or simple resource usageJobs are added to a work queueAs threads become idle, they take jobs off the work queue and execute them
Many variations on this theme!
Checking for Work
How can a thread wait for jobs to show up?Could sit in a loop, polling the work queue
// Get a job.Job *pJob = 0;do {mutex.lock();if (workQueue.size() > 0)pJob = workQueue.pop_front();
mutex.unlock();} while (pJob == 0);
Very inefficient approach!Will consume way too many CPU resourcesWill actually hinder progress of other threads!
Passive Waiting
Would like to put a waiting thread to sleepIt stops running, or consuming any CPU resourcesThread is suspended
Later, wake the thread upWhen some condition becomes true, the thread can resume its computatione.g. when work queue receives another job, wake up a worker thread
POSIX Condition Variables
Condition variables provide this kind of interaction
A thread can wait for a condition to become trueAnother thread can signal when the condition becomes true
Also supports multiple-thread communicationSeveral threads can wait for a condition to become trueA thread can broadcast when the condition becomes true
Condition Variables and Mutexes
Condition variables have an associated mutexThe condition applies to shared state…The mutex is used for guarding that shared state
Waiting thread procedure:Lock the mutexCheck for desired conditionIf condition isn’t true, wait on the condition variable
Mutex is automatically unlocked when thread waits on condition variable
…so some other thread can acquire the mutex and act on the shared state
When waiting thread is waken up:Automatically reacquires the mutex before proceeding
Signaling on a Condition Variable
Signaling thread procedure:Lock the mutexModify the shared state
Now the condition should be trueSignal on the condition variableUnlock the mutex
When thread signals on condition variable, one waiting thread wakes up
That thread can’t progress until signaling thread actually unlocks the mutexRemember: waiting thread must reacquire the mutex lock before continuing on.
Wakeups
For waiting threads, important to check condition in a loop
Waiting on a condition variable can generate spurious wakeups!Also, if multiple threads are signaled, condition may be false again by the time a particular thread gets to the mutex
Follow this pattern:Lock mutexWhile condition isn’t true:
Wait on condition variableUnlock mutex
Condition Variable API
Type: pthread_cond_tTo initialize:int pthread_cond_init(pthread_cond_t *,
pthread_condattr_t *)To clean up:int pthread_cond_destroy(pthread_cond_t *)To wait:int pthread_cond_wait(pthread_cond_t *,
pthread_mutex_t *)Also pthread_cond_timedwait(...) too
To signal:int pthread_cond_signal(pthread_cond_t *)To broadcast:int pthread_cond_broadcast(pthread_cond_t *)
Condition Variable Wrapper
Would like to create a C++ wrapper class for condition variables too
Resource Allocation Is Initialization patternSimplifying constraint:
Associate a condition variable with one mutexTechnically, can use a condition variable with multiple mutexes, but is tricky and bug-prone!
Pass a Mutex-reference to ConditionVariableconstructor
When someone calls wait(), use specified Mutex
Accessing the Mutex
Problem:Condition variable needs a pthread_mutex_t pointer in order to waitMutex class declares its pthread_mutex_t variable as privateShouldn’t expose this state, if at all possible!
Solution: use the friend access modifierMutex declares ConditionVariable to be a friend classConditionVariable can access private members of Mutex
friend and Encapsulation
friend modifier should be used very rarely!Allows encapsulation to be violated!Other classes and functions can access or mutate private state values
In this case:Mutex and ConditionVariable violate encapsulation with respect to each other……but doing so allows Mutex to preserve encapsulation towards other classes
friend Syntax
Declare friend classes and functions in class declaration
class Mutex {// Condition variable accesses POSIX// mutex variable directly.friend class ConditionVariable;
pthread_mutex_t m;public:
...};
Friend class can access private stateAlways document reasons for this!!!
friend Syntax (2)
Can also declare friend functions:class Foo {
friend int bar(const Foo &f);
int secret;public:
...};
int bar(const Foo &f) {cout << f.secret << endl;
}
Again, only use when absolutely necessary
Pool Threads
Thread-pool threads have a simple existence:Wait for a job to show up.When a job arrives, do it.Wait for more jobs to show up.
ThreadPool will be a classInstantiate the class to make a thread pool to use
Problem: How to specify the thread function?Need a pointer to some thread functionCan’t use a member function, since that would also require an object
Static Functions
All pool threads will have same behaviorRegardless of what thread-pool they are a part of!
ThreadPool class can declare a function to use for pool threadsDeclare the function static, to indicate that it’s not part of an object
Static function is still part of the class it’s inIt can access private members, etc.Don’t call static function on an objectthis is not defined in static function’s body
Static Function Syntax
Declare static function in ThreadPool declarationclass ThreadPool {
private deque<Job *> workQueue;...static void *threadFunc(void *);
public:...
};
Thread function is private: it is encapsulated within the ThreadPool classCan pass thread function to threads created in the poolCan pass a pointer to the ThreadPool object as the thread function’s argument
Static Function Implementation
Inside static function, can use ThreadPool statevoid * ThreadPool::threadFunc(void *arg) {
assert(arg != 0);ThreadPool *pool = (ThreadPool *) arg;
while (true) {pool->mutex.lock();while (pool->workQueue.size() == 0) {
...}pool->mutex.unlock();
}}Don’t need to say “static” in function definition
Pool Queues
Need a queue for incomplete jobsJobs are represented as an abstract class
Provide implementation of abstract functions, to implement specific kinds of jobsThread pool can be coded against generic Job base-class
Can also use a queue for completed jobsIf a job involves computations, might need to retrieve computed results when job is completed
The Standard Template Library
Use Standard Template Library collections in thread pool
Generic, customizable template classesManage collections of objects
Different STL collections have different characteristics
Functional differencesDifferent performance characteristics
STL Containers
Sequences store their elements in linear sequential order
Variable size; can grow or shrinkvector – random access, constant append time, linear insert time, linear prepend timedeque – like vector, but constant prepend time toolist – doubly linked list, constant insert anywhere, only sequential accessslist – singly linked list, only forward traversalbit_vector – vector of bools, optimized for space!
Associative Containers
Support efficient retrieval based on keysNo support for inserting at a specific position
set – stores keys; each appears only oncemap – stores (key,value) pairs; each key appears only oncemultiset, multimap – like the above, but keys can appear multiple timesThese are Sorted Associative Containers
They don’t hash the keys! Most operations are O(log(N))But, they do keep their entries sorted by key.
Job Queues
Thread-pool users will append jobs on end of the queuePool threads will pull jobs from front of queueGood candidates for work queue would be deque or listdeque is probably fastest and easiest to use
Pool threads also need stored and managedCan use vector for this since count doesn’t need to change
Element-Access Operations
Sequences provide many kinds of element-access operationsCan use [] operator to retrieve individual elements
vector<int> v(3); // Vector of 3 elementsv[0] = 7;v[1] = v[0] + 3;v[2] = v[0] + v[1];
Can use push_back() and push_front() to add new elements
vector<Thread*> threads;for (int i = 0; i < numThreads; i++)
threads.push_back(new Thread(...));pop_back() and pop_front() to remove elements, etc.
Iterating Over Collections
All collections provide iterators for traversing their contentsAn iterator is a generalization of a pointer
Iterators provide the same operations as pointers(Pointers and iterators can be used in many similar ways)
An iterator points to a specific element of a collectionDereferencing the iterator returns the element it points toIncrementing an iterator moves it to the next element
Example:vector<Thread*>::iterator it;for (it = threads.begin(); it != threads.end(); it++){(*it)->join(); // Type of *it is Thread*delete *it;
}
Iterating over Collections (2)
Our example code:vector<Thread*>::iterator it;for (it = threads.begin(); it != threads.end(); it++){(*it)->join(); // Type of *it is Thread*delete *it;
}All collections provide nested typedefs for iterators
vector<Thread*>::iterator is an iterator for a vector of Thread* elementsvector<Thread*>::const_iterator is a read-only iterator, if the vector itself is const
All collections provide begin() and end() accessorsbegin() returns an iterator pointing to first elementend() returns an iterator pointing just past the last element
STL Resources
Complete STL reference documentation available onlineThe SGI STL Documentation
http://www.sgi.com/tech/stl/index.html
Feel free to experiment with the STL in your assignment
Very feature-rich set of templates and operations
Using the Thread Pool
This week you will just test the thread poolCreate a simple “test job” class, similar to POSIX “Hello world” functionalityMake the test job pause for some amount of timeMake sure the pool actually runs jobs in parallelMake sure the thread pool shuts down correctly
Next week:Plug your thread pool into a Mandelbrot fractal drawing programParallelize the generator to take advantage of multiple CPUs