Wednesday, May 8, 2013

Boost Threads (Part 2)


/**
 * This blog entry focuses on the use of a mutex.
 */

// Obviously required for using threads.
#include <boost/thread.hpp>

// Output through cout can be garbled when using
// threads, so in this example I'm going to guard
// against cout interruption.
#include <iostream>

// Using this to put together output strings.
#include <sstream>

/**
 * If you are building this example, try commenting
 * this out and see what happens to the output.
 * It's hard to tell at times but it is indeed
 * garbled at points.
 */
#define DO_LOCK

// We have a singular mutex that guards cout. This
// is only used from PrintLocker.
static boost::mutex printMutex;


/**
 * The PrintLocker. This is an RAII class that
 * locks a mutex (if it can) and then unlocks it
 * upon destruction. This is very exception
 * savvy and will earn you extra points with your
 * colleagues.
 */
struct PrintLocker
{
PrintLocker()
{
#if defined DO_LOCK
printMutex.lock();
#endif
}

~PrintLocker()
{
#if defined DO_LOCK
printMutex.unlock();
#endif
}
};


/**
 * This is the only print mechanism this program
 * has. It guards the cout access.
 */
void print(std::string const& stringOutput)
{
// Check it out, by creating this object
// you lock the mutex (if you can get it).
// If you can't get it, the thread yields
// and allows other active threads to run.
PrintLocker pl;

// Once you have the lock, feel free to
// print to standard out.
std::cout << stringOutput << std::endl;

// At the end, the object pl will be
// destroyed. Remember that in the destructor
// the mutex will unlock. If an exception
// is thrown within cout then pl will still
// be destroyed and the mutex unlocked.
}


/**
 * This is just a convenience function used
 * to put together output strings via a string
 * stream.
 */
template <typename T1, typename T2, typename T3>
std::string getString(T1 const& t1, T2 const& t2, T3 const& t3)
{
std::stringstream ss;
ss << t1 << t2 << t3;
return ss.str();
}


/**
 * The new thread function, which is invoked whenever
 * we create a new boost thread. It will pause randomly
 * while iterating 10 times. The random pauses help us
 * when trying to expose race conditions (just comment
 * out the #define DO_LOCK).
 */
void newThread(int threadId)
{
print(getString("START Thread #", threadId, ""));

for(int i=0; i<10; ++i)
{
print(getString(i, "th Iteration, Thread #", threadId));

boost::posix_time::seconds pauseTime( rand() % 2 );
boost::this_thread::sleep(pauseTime);
}

print(getString("END Thread #", threadId, ""));
}


int main(int argc, char** argv)
{

boost::thread t1(newThread, 1);
boost::thread t2(newThread, 2);
boost::thread t3(newThread, 3);

t1.join();
t2.join();
t3.join();
}


/**
 * In conclusion, boost mutexes are pretty normal.
 * They provide a nice wrapper around whatever threading
 * system is already present on your system.
 */

No comments:

Post a Comment