Important Note:<o:p>
This article presents the old (0.3) version of Boost.Application (Library Proposal to boost.org)!<o:p>
A new version (0.4) with other interface is already available to download on:
https://github.com/retf/Boost.Application<o:p>
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
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)
{
vector< vector<double> > kernel2d ( 2*kernelRadius+1, vector<double>(2*kernelRadius+1) );
double sigma = kernelRadius/2.;
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];
}
}
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)
{
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
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)
{
task_count_ = 0;
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_;
};
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.:
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.