使用 Boost.Asio、Boost.Thread 和 Boost.Application 创建工作队列(线程池)应用程序






4.75/5 (7投票s)
本文介绍了一个用于构建工作队列应用程序的 Boost.Application、Boost.Asio 和 Boost.Thread 库。
重要提示
本文介绍了 Boost.Application 的旧版本(0.3)(Boost.org 的库提案)!
一个新版本(0.4),具有不同的接口,已可在以下网址下载:
https://github.com/retf/Boost.Application
请注意,0.3 版本不再维护!0.4 版本现在正在维护并定期更新。
引言
本文介绍了如何使用 Boost.Thread 和 Boost.Asio 结合 Boost.Application 来构建一个工作队列(线程池)应用程序。请注意,Boost.Application 尚未成为官方的 Boost C++ 库。
“Boost.Application” 为任何希望构建 Windows 或 Unix 变体(例如 Linux、MacOS)系统应用程序的开发人员提供了一个应用程序环境或起点。
本文是文章的续篇: https://codeproject.org.cn/Articles/662221/Create-a-Windows-Service-Application-Using-the-Boo
反馈
如果您是 Boost 用户,并且使用 Boost 邮件列表,请直接在列表中提供您对该库的反馈。(请注明您是否认为该库应被接受为 boost.com 的一部分)。 https://boost.ac.cn/community/groups.html
如果您是 CodeProject 用户,请在此页面直接提供您对库的反馈。
Bug
如果您发现任何 BUG,请发送至: re.tf@acm.org
1. 基本设置
请参阅https://codeproject.org.cn/Articles/662221/Create-a-Windows-Service-Application-Using-the-Boo 的主题 1(Boost.Application 安装)和主题 4(在 Visual Studio 中创建应用程序骨架),了解如何准备好您的开发环境。
2. Boost.Application 简介
Boost.Application 主要支持两种应用程序类型:
本文专注于“通用应用程序”。“通用应用程序”类型将用于打包基于 Boost.Asio 和 Boost.Thread 构建的线程池。
请查阅库手册以了解 Boost.Application 提供的其他功能,手册可在以下位置访问:boost_installation\libs\application\doc\html\index.html。
文档处于 alpha 状态。如果您是英语母语者,并希望为文档做出贡献,或者想为库贡献新功能(请参阅文档中的未来工作),请与我联系。
3. 示例应用程序项目
本文提供了一个教程,展示如何使用 Boost.Application 库构建“通用应用程序”类型;本文对其他 Boost 库的介绍级别较低,例如
- Boost.Thread;
- Boost.System;
- Boost.Asio。
应用程序的工作是计算“高斯模糊(图像模糊滤镜)”,因此应用程序将启动 'n' 个任务以在线程池中并行计算。
4. 通用应用程序
4.1 通用应用程序的基础如下
#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 应用程序类的自然/扩展接口
Boost.Application 提供了两种客户端应用程序类接口:
- 自然接口;
- 扩展接口。
自然接口隐藏了许多细节,并提供了一种简单便捷的方式来构建通用应用程序。如果用户需要更多控制,则必须使用扩展接口。
上面的示例使用了“自然接口”,有关扩展接口的更多信息,请参阅库文档。
4.3 错误处理程序
Boost.Application 提供了两种错误处理方式:一种抛出类型为 boost::system::system_error
的异常,另一种接收一个 boost::system::error_code
变量 ec
,该变量将被设置为操作结果,而不抛出异常。
在上一篇文章中已经介绍了抛出异常的版本,这里将展示使用 'ec' 的版本。
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. 构建工作者
工作者将计算“高斯模糊(图像模糊滤镜)”,它必须是一个函数对象。如果愿意,您可以在此处更改“高斯模糊”的行为。
5.1 高斯模糊工作者
// 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. 构建工作队列(线程池)
我们将使用 Boost.Asio 和 Boost.Thread 来构建工作队列。
asio 的 io_service
将持有一个线程池,并将任务发送给它,然后由池中的一个线程执行。
任务必须是一个函数对象,因此 gaussian_blur
是一个函数对象类。
6.1 工作队列
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. 将所有部分组装到 Boost.Application 中
现在是时候将所有部分组合到 Boost.Application
函数对象类中了。
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)
{
// 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
myapp
(Application
类)从 work_queue
继承。work_queue
将具有与机器上的处理器(核心)相同的线程数。您可以在此处指定不同的数量,例如:work_queue<10>
。这将导致池持有 10 个线程。
“add_result”是接收任务结果的回调方法。它持有一个计数器(task_count_
),使我们能够知道何时所有工作都已完成。
7.2 向池添加任务
我们在应用程序函数对象中执行此操作。添加了三个任务。例如:
// 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 )));
这里我们可以有任意数量的任务,具有任意类型的工作者。
7.3 执行
当我们的应用程序执行时,我们会看到类似这样的内容:
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. 结论
在本文中,我展示了 Boost.Application 可以轻松地与其他库集成/扩展。由于 Boost.Application 的设计,用户可以轻松地为函数对象类添加其他功能。