Getting Started with Boost Threads in Visual Studio

1. Worker thread using functors

In this example we achieve exactly the same worker threads but this time by using functors, which are objects that can be called in the same way as functions, and are defined by overloading the operator(). For more information on Functors see this post.

#include <iostream>
#include <boost/thread.hpp>
#include <boost/date_time.hpp>

class Worker
{
public:
	void operator()()
	{
		boost::posix_time::seconds workTime(3);         
		std::cout << "workerFunc: running" << std::endl;
         
		// Pretend to do something useful...
		boost::this_thread::sleep(workTime);         
		std::cout << "workerFunc: finished" << std::endl;
	}
};        
	
int main(int argc, char* argv[])
{
    std::cout << "main: startup" << std::endl;
         
	// Boost thread constructor
	Worker worker;
    boost::thread workerThread(worker);		         
    std::cout << "main: waiting for thread" << std::endl;
         
	// Causes main() thread to sleep until worker thread(s) completed
	workerThread.join();	       		         
    std::cout << "main: done" << std::endl;
         
    return 0;
}

2. Worker threads through object methods

Here we define an object's instance method to run on its own thread. The only significant difference to that of making a regular function into a thread is that we specify the method using its class qualifier and we use the & operator to pass the address of the method, along with any other arguments it may need.

#include <iostream>
#include <boost/thread.hpp>
#include <boost/date_time.hpp>

class Worker
{
public:
	void DoStuff(const int& time, const char* msg)
	{
		boost::posix_time::seconds workTime(time);         
		std::cout << msg << " running" << std::endl;
         
		// Pretend to do something useful...
		boost::this_thread::sleep(workTime);         
		std::cout << msg << " finished" << std::endl;
	}
};        
	
int main(int argc, char* argv[])
{
    std::cout << "main: startup" << std::endl;
         
	// Boost thread constructor
	Worker worker;
    //boost::thread workerThread(worker);		
	boost::thread workerThread(&Worker::DoStuff, &worker, 3, "Worker thread");
    std::cout << "main: waiting for thread" << std::endl;
         
	// Causes main() thread to sleep until worker thread(s) completed
	workerThread.join();	       		         
    std::cout << "main: done" << std::endl;
         
    return 0;
}

3. Managing threads within the object

You may wish your objects to be able to encapsulate and hence manage their own threads. In this case we keep the thread as a data member, which gets assigned its value in a start() method. The default constructor for boost::thread maintains it in an invalid state until it is actually assigned it's own value. The only real difference in the assignement is that we pass it the this value.

#include <iostream>
#include <boost/thread.hpp&gt
#include <boost/date_time.hpp&gt

class Worker
{
public:

	void start()
	{
		m_Thread = boost::thread( 
							&Worker::DoStuff, 
			                this, 
							3, 
							"Worker thread" );
	}

	void join()
	{
		m_Thread.join();
	}

	void DoStuff(const int& time, const char* msg)
	{
		boost::posix_time::seconds workTime(time);         
		std::cout << msg << " running" << std::endl;
         
		// Pretend to do something useful...
		boost::this_thread::sleep(workTime);         
		std::cout << msg << " finished" << std::endl;
	}

private:
	boost::thread m_Thread;
};        
	
int main(int argc, char* argv[])
{
    std::cout << "main: startup" << std::endl;         	
	
	Worker worker;
	worker.start();

    std::cout << "main: waiting for thread" << std::endl;
         	
	worker.join();	       		         
    std::cout << "main: done" << std::endl;
         
    return 0;
}

4. Using mutexes to ensure exclusive access to resources

#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <iostream>

class SharedResource
{
private:
	int value;
	boost::mutex mtx;

public:

	SharedResource(const int& val) : value(val) {}

	void Add(const int& val)
	{ 		
		mtx.lock();		
		std::cout << "\nLocked Add 2";
		value += val;
		std::cout << "\nCurrent value = " << value;
		mtx.unlock();
		std::cout << "\nUnlocked Add 2";
	}

	void Subtract(const int& val)
	{		
		mtx.lock();		
		std::cout << "\nLocked Subtract 1";
		value -= val;
		std::cout << "\nCurrent value = " << value;
		mtx.unlock();
		std::cout << "\nUnlocked Subtract 1";
	}

	int Value()
	{		
		mtx.lock();		
		std::cout << "\nLocked Value";
		int v = value;
		std::cout << "\nCurrent value = " << value;
		mtx.unlock();
		std::cout << "\nUnlocked Value";

		return value;
	}
};

SharedResource sharedResource( 5 );

void Worker1()
{
	for ( int i = 4; i > 0; --i ) 
	{		
		sharedResource.Add( 2 );		
	}
}

void Worker2()
{
	for ( int i = 4; i > 0; --i ) 
	{		
		sharedResource.Subtract( 1 );		
	}
}

int main(int, char*[])
{
	boost::thread thread1( Worker1 ); // start concurrent execution of Worker1
    boost::thread thread2( Worker2 ); // start concurrent execution of Worker2
   
	thread1.join();
    thread2.join();	
   
    return 0;
}

Giving the following output:

Locked Add 2
Current value = 7
Unlocked Add 2
Locked Subtract 1
Current value = 6
Locked Add 2
Current value = 8
Unlocked Subtract 1
Locked Subtract 1
Current value = 7
Unlocked Subtract 1
Unlocked Add 2
Locked Subtract 1
Current value = 6
Unlocked Subtract 1
Locked Add 2
Current value = 8
Unlocked Add 2
Locked Subtract 1
Current value = 7
Unlocked Subtract 1
Locked Add 2
Current value = 9
Unlocked Add 2