常规编程
现在阅读
Creating a Work Queue (Thread Pool) Application Using Boost.Asio, Boost.Thread, and Boost.Application
0

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

由 ultracpy2018年1月26日

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

// 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.

出处:https://www.codeproject.com/Articles/664709/Creating-a-Work-Queue-Thread-Pool-Application-Usin

关于作者
ultracpy
评论

你必须 登录 提交评论