Creating a Work Queue (Thread Pool) Application Using Boost.Asio, Boost.Thread, and Boost.Application






4.75/5 (7 votes)
This article present a Boost.Application, Boost.Asio, and Boost.Thread library used to build a Work Queue application.
Important Note:
This article presents the old (0.3) version of Boost.Application (Library Proposal to boost.org)!
A new version (0.4) with other interface is already available to download on:
https://github.com/retf/Boost.Application
Note that version 0.3 is no longer maintained! Version 0.4, is now maintained and receives regular updates.
Introduction
This article presents how to use Boost.Thread and Boost.Asio with Boost.Application to build a Work Queue (Threadpool) application. Note that Boost.Application is not yet an official Boost C++ library.
A “Boost.Application” provides an application environment, or start point to any people that want a basic infrastructure to build a system application on Windows or Unix Variants (e.g. Linux, MacOS).
This article is a continuation of the article: http://www.codeproject.com/Articles/662221/Create-a-Windows-Service-Application-Using-the-Boo
Feedback
If you are boost user, and use Boost Mailing Lists, please provide your feedback about the library, directly on list. (Specify if you think if the library should be accepted as part of boost.com). http://www.boost.org/community/groups.html
If you are a CodeProject user, please provide your feedback about library direct in this page.
Bugs
If you find any BUG, please, send it to me at: re.tf@acm.org
1. Basic Setup
Please refer topics 1 (Boost.Application Installation) and 4 (Create Application Skeleton on Visual Studio) of http://www.codeproject.com/Articles/662221/Create-a-Windows-Service-Application-Using-the-Boo to know how get your environment ready to use.
2. Boost.Application Introduction
Boost.Application supports basically two flavors of applications that can be:
- Common Application: This kind of application is a usual Interactive Terminal/Console Application.
- Server Application: This kind of application generates a Service (Windows), or a background process/Daemon (Unix).
This article is focused on “Common Application”. The “Common Application” type will be used to pack a thread pool builds on top of Boost.Asio and Boost.Thread.
Check library manual to know more about other features provided in Boost.Application, the manual can be accessed on: boost_installation\libs\application\doc\html\index.html.
The documentation is in alpha status. If you are a native English speaker and would like to contribute to the documentation, or want contribute with some new functionality for libraries (see future work on documentation), please contact me.
3. Sample Application Project
This article provides a how to tutorial that shows how to use Boost.Application library to build a “common application” type; this article cover in less detail level others boost library’s like:
- Boost.Thread;
- Boost.System;
- Boost.Asio.
The job of application is calculate a “Gaussian Blur (image-blurring filter)”, thus the application will launch 'n' tasks to calculate in parallel using a thread pool.
4. Common Application
4.1 The base of common application looks like
#include <boost\application.hpp>
class myapp
{
public:
int operator()(const std::vector< application_ctrl::string_type >& args,
application_ctrl& ctrl)
{
ctrl.wait_for_termination_request();
return 0;
}
};
int main(int argc, char* argv[])
{
return application::common_app<myapp>( application::args(argc, argv) )();
}
4.2 Application Class Natural/Extended Interface
Boost.Application provides two kinds of client application class interface:
- Natural Interface;
- Extended Interface.
The natural interface hides a lot of details and provides an easy and simple way to build a common application. If user need more control, the extended interface must be used.
The above example use “Natural Interface” to know more about extended interface refer to library documentation.
4.3 Error Handler
Boost.Application
provides two ways to handle errors; one throws an exception of
type boost::system::system_error
and another receives a
boost::system::error_code
variable ec
that would be set to the
result of the operation, and no thrown an exception.
In the last article the version that throws an exception has been addressed, here the version that uses ‘ec’ is shown
int main(int argc, char* argv[])
{
boost::system::error_code ec;
int ret = application::common_app<myapp>( application::args(argc, argv), ec )();
if(ec)
{
std::cerr << ec.message() << std::endl;
}
return ret;
}
5. Build a worker
The worker will calculate the “Gaussian Blur (image-blurring filter)”, it must be a functor. If you want you can change the behavior of “Gaussian Blur” to any thing here.
5.1 Gaussian Blur Worker
// worker class that calculate gaussian blur
// http://en.wikipedia.org/wiki/Gaussian_blur
template< int kernelRadius = 3>
struct gaussian_blur
{
typedef boost::function< void (vector< vector<double> >) > callback;
gaussian_blur(const callback& cb)
: callback_(cb)
{
}
void operator()()
{
boost::timer::cpu_timer timer;
kernel2d_ = produce_gaussian_kernel(kernelRadius);
boost::timer::cpu_times const elapsed_times(timer.elapsed());
std::cout
<< "gaussian_blur takes:"
<< format(elapsed_times, 9)
<< ", for size: "
<< kernelRadius
<< std::endl;
callback_(kernel2d_);
}
protected:
double gaussian (double x, double mu, double sigma)
{
return exp( -(((x-mu)/(sigma))*((x-mu)/(sigma)))/2.0 );
}
vector< vector<double> > produce_gaussian_kernel (int kernelRadius)
{
// get kernel matrix
vector< vector<double> > kernel2d ( 2*kernelRadius+1, vector<double>(2*kernelRadius+1) );
// determine sigma
double sigma = kernelRadius/2.;
// fill values
double sum = 0;
for (int row = 0; row < kernel2d.size(); row++)
{
for (int col = 0; col < kernel2d[row].size(); col++)
{
kernel2d[row][col] = gaussian(row, kernelRadius, sigma) * gaussian(col, kernelRadius, sigma);
sum += kernel2d[row][col];
}
}
// normalize kernel, or the image becomes dark
for (int row = 0; row < kernel2d.size(); row++)
for (int col = 0; col < kernel2d[row].size(); col++)
kernel2d[row][col] /= sum;
return kernel2d;
}
private:
callback callback_;
vector< vector<double> > kernel2d_;
};
6. Build a work queue (thread-pool)
To build a work queue, the Boost.Asio and Boost.Thread will be used.
The asio io_service
we will hold a pool of threads, and a task will be sent to it, and will get
executed by one of the threads in the pool.
The task must be
a functor, because of this the gaussian_blur
is a functor class.
6.1 Work Queue
template <int NWorkers = 0>
class work_queue
{
public:
work_queue()
{
work_ctrl_ = new boost::asio::io_service::work(io_service_);
int workers = boost::thread::hardware_concurrency();
if(NWorkers > 0)
workers = NWorkers;
for (std::size_t i = 0; i < workers; ++i)
{
threads_.create_thread(boost::bind(&asio::io_service::run, &io_service_));
}
}
virtual ~work_queue()
{
delete work_ctrl_;
}
template <typename TTask>
void add_task(TTask task)
{
// c++11
// io_service_.dispatch(std::move(task));
io_service_.dispatch(task);
}
private:
boost::asio::io_service io_service_;
boost::thread_group threads_;
boost::asio::io_service::work *work_ctrl_;
};
7. Assembly all parts on Boost.Application
Now is time to
join all parts on the Boost.Application
functor class.
7.1 Application Class
// application class
class myapp : work_queue<0>
{
public:
void add_result(vector< vector<double> > kernel2d)
{
boost::lock_guard<boost::mutex> lock(mutex_);
task_count_++;
result_.push_back(kernel2d);
if(task_count_== 3)
{
cout << "all tasks are completed, waiting ctrl-c to display the results..." << endl;
}
}
int operator()(const std::vector< application::application_ctrl::string_type >& args,
application::application_ctrl& ctrl)
{
// your application logic here!
task_count_ = 0;
// our tasks
add_task(gaussian_blur<3>( boost::bind<void>( &myapp::add_result, this, _1 )));
add_task(gaussian_blur<6>( boost::bind<void>( &myapp::add_result, this, _1 )));
add_task(gaussian_blur<9>( boost::bind<void>( &myapp::add_result, this, _1 )));
ctrl.wait_for_termination_request();
return 0;
}
int stop()
{
std::cout << "Result..." << std::endl;
for(int i = 0; i < result_.size(); ++i)
{
cout << i << " : -----------------------" << std::endl;
vector< vector<double> > & kernel2d = result_[i];
for (int row = 0; row < kernel2d.size(); row++)
{
for (int col = 0; col < kernel2d[row].size(); col++)
{
cout << setprecision(5) << fixed << kernel2d[row][col] << " ";
}
cout << endl;
}
}
return 1;
}
private:
boost::mutex mutex_;
vector< vector< vector<double> > > result_;
int task_count_;
}; // myapp
The myapp
(Application
class) was extended from work_queue
. The
work_queue
will have will
have the same number of threads that the processor (cores) on machine. You can
specify a different number here, e.g.: work_queue<10>
. This cause
to the pool hold 10 threads.
The “add_result”
is the callback method that will receive the result of tasks. It holds a
counter (task_count_
) that enable we know when all work is done.
7.2 Adding task to pool
We do this on application functor. Tree tasks are added. E.g.:
// our tasks
add_task(gaussian_blur<3>( boost::bind<void>( &myapp::add_result, this, _1 )));
add_task(gaussian_blur<6>( boost::bind<void>( &myapp::add_result, this, _1 )));
add_task(gaussian_blur<9>( boost::bind<void>( &myapp::add_result, this, _1 )));
Here we can have any number of tasks, whit any type of workers.
7.3 Execution
When our application is executed we will get some think like this:
gaussian_blur takes: 0.000143487s wall, 0.000000000s user + 0.000000000s system= 0.000000000s CPU (n/a%), for size: 3
gaussian_blur takes: 0.000330720s wall, 0.000000000s user + 0.000000000s system= 0.000000000s CPU (n/a%), for size: 6
gaussian_blur takes: 0.000581997s wall, 0.000000000s user + 0.000000000s system= 0.000000000s CPU (n/a%), for size: 9
all tasks are completed, waiting ctrl-c to display the results...
Result...
0 : -----------------------
0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134
0.00408 0.01238 0.02412 0.03012 0.02412 0.01238 0.00408
0.00794 0.02412 0.04698 0.05867 0.04698 0.02412 0.00794
0.00992 0.03012 0.05867 0.07327 0.05867 0.03012 0.00992
0.00794 0.02412 0.04698 0.05867 0.04698 0.02412 0.00794
0.00408 0.01238 0.02412 0.03012 0.02412 0.01238 0.00408
0.00134 0.00408 0.00794 0.00992 0.00794 0.00408 0.00134
1 : -----------------------
0.00034 0.00063 0.00104 0.00154 0.00203 0.00240 0.00254 0.00240 0.00203 0.00154 0.00104 0.00063 0.00034
0.00063 0.00117 0.00192 0.00284 0.00375 0.00443 0.00468 0.00443 0.00375 0.00284 0.00192 0.00117 0.00063
0.00104 0.00192 0.00317 0.00468 0.00618 0.00730 0.00772 0.00730 0.00618 0.00468 0.00317 0.00192 0.00104
0.00154 0.00284 0.00468 0.00691 0.00912 0.01077 0.01139 0.01077 0.00912 0.00691 0.00468 0.00284 0.00154
0.00203 0.00375 0.00618 0.00912 0.01204 0.01422 0.01503 0.01422 0.01204 0.00912 0.00618 0.00375 0.00203
0.00240 0.00443 0.00730 0.01077 0.01422 0.01680 0.01776 0.01680 0.01422 0.01077 0.00730 0.00443 0.00240
0.00254 0.00468 0.00772 0.01139 0.01503 0.01776 0.01878 0.01776 0.01503 0.01139 0.00772 0.00468 0.00254
0.00240 0.00443 0.00730 0.01077 0.01422 0.01680 0.01776 0.01680 0.01422 0.01077 0.00730 0.00443 0.00240
0.00203 0.00375 0.00618 0.00912 0.01204 0.01422 0.01503 0.01422 0.01204 0.00912 0.00618 0.00375 0.00203
0.00154 0.00284 0.00468 0.00691 0.00912 0.01077 0.01139 0.01077 0.00912 0.00691 0.00468 0.00284 0.00154
0.00104 0.00192 0.00317 0.00468 0.00618 0.00730 0.00772 0.00730 0.00618 0.00468 0.00317 0.00192 0.00104
0.00063 0.00117 0.00192 0.00284 0.00375 0.00443 0.00468 0.00443 0.00375 0.00284 0.00192 0.00117 0.00063
0.00034 0.00063 0.00104 0.00154 0.00203 0.00240 0.00254 0.00240 0.00203 0.00154 0.00104 0.00063 0.00034
2 : -----------------------
0.00015 0.00023 0.00034 0.00047 0.00062 0.00077 0.00091 0.00103 0.00111 0.00114 0.00111 0.00103 0.00091 0.00077 0.00062 0.00047 0.00034 0.00023 0.00015
0.00023 0.00036 0.00052 0.00071 0.00094 0.00117 0.00139 0.00157 0.00169 0.00174 0.00169 0.00157 0.00139 0.00117 0.00094 0.00071 0.00052 0.00036 0.00023
0.00034 0.00052 0.00075 0.00103 0.00136 0.00169 0.00201 0.00228 0.00245 0.00251 0.00245 0.00228 0.00201 0.00169 0.00136 0.00103 0.00075 0.00052 0.00034
0.00047 0.00071 0.00103 0.00142 0.00187 0.00233 0.00277 0.00314 0.00338 0.00347 0.00338 0.00314 0.00277 0.00233 0.00187 0.00142 0.00103 0.00071 0.00047
0.00062 0.00094 0.00136 0.00187 0.00245 0.00306 0.00364 0.00412 0.00444 0.00455 0.00444 0.00412 0.00364 0.00306 0.00245 0.00187 0.00136 0.00094 0.00062
0.00077 0.00117 0.00169 0.00233 0.00306 0.00383 0.00455 0.00514 0.00554 0.00568 0.00554 0.00514 0.00455 0.00383 0.00306 0.00233 0.00169 0.00117 0.00077
0.00091 0.00139 0.00201 0.00277 0.00364 0.00455 0.00540 0.00611 0.00659 0.00675 0.00659 0.00611 0.00540 0.00455 0.00364 0.00277 0.00201 0.00139 0.00091
0.00103 0.00157 0.00228 0.00314 0.00412 0.00514 0.00611 0.00692 0.00745 0.00764 0.00745 0.00692 0.00611 0.00514 0.00412 0.00314 0.00228 0.00157 0.00103
0.00111 0.00169 0.00245 0.00338 0.00444 0.00554 0.00659 0.00745 0.00802 0.00822 0.00802 0.00745 0.00659 0.00554 0.00444 0.00338 0.00245 0.00169 0.00111
0.00114 0.00174 0.00251 0.00347 0.00455 0.00568 0.00675 0.00764 0.00822 0.00843 0.00822 0.00764 0.00675 0.00568 0.00455 0.00347 0.00251 0.00174 0.00114
0.00111 0.00169 0.00245 0.00338 0.00444 0.00554 0.00659 0.00745 0.00802 0.00822 0.00802 0.00745 0.00659 0.00554 0.00444 0.00338 0.00245 0.00169 0.00111
0.00103 0.00157 0.00228 0.00314 0.00412 0.00514 0.00611 0.00692 0.00745 0.00764 0.00745 0.00692 0.00611 0.00514 0.00412 0.00314 0.00228 0.00157 0.00103
0.00091 0.00139 0.00201 0.00277 0.00364 0.00455 0.00540 0.00611 0.00659 0.00675 0.00659 0.00611 0.00540 0.00455 0.00364 0.00277 0.00201 0.00139 0.00091
0.00077 0.00117 0.00169 0.00233 0.00306 0.00383 0.00455 0.00514 0.00554 0.00568 0.00554 0.00514 0.00455 0.00383 0.00306 0.00233 0.00169 0.00117 0.00077
0.00062 0.00094 0.00136 0.00187 0.00245 0.00306 0.00364 0.00412 0.00444 0.00455 0.00444 0.00412 0.00364 0.00306 0.00245 0.00187 0.00136 0.00094 0.00062
0.00047 0.00071 0.00103 0.00142 0.00187 0.00233 0.00277 0.00314 0.00338 0.00347 0.00338 0.00314 0.00277 0.00233 0.00187 0.00142 0.00103 0.00071 0.00047
0.00034 0.00052 0.00075 0.00103 0.00136 0.00169 0.00201 0.00228 0.00245 0.00251 0.00245 0.00228 0.00201 0.00169 0.00136 0.00103 0.00075 0.00052 0.00034
0.00023 0.00036 0.00052 0.00071 0.00094 0.00117 0.00139 0.00157 0.00169 0.00174 0.00169 0.00157 0.00139 0.00117 0.00094 0.00071 0.00052 0.00036 0.00023
0.00015 0.00023 0.00034 0.00047 0.00062 0.00077 0.00091 0.00103 0.00111 0.00114 0.00111 0.00103 0.00091 0.00077 0.00062 0.00047 0.00034 0.00023 0.00015
Press any key to continue . . .
8. Conclusion
In this article I showed that Boost.Application can be integrated/extended easily with other libraries. Due to the design of Boost.Application, the user can add other features to functor class easily.