github两个实现
-
https://github.com/progschj/ThreadPool
- https://github.com/mtrebi/thread-pool
最先找到第一个的,看了一头雾水,过于难懂,第二个和第一个实现有些地方一样,但是文档写的很全。
其中enqueue的实现在看了example的代码之后,因为不熟悉拉姆达表达式,造成了很大的困扰。
ThreadPool pool(4);
std::vector< std::future<int> > results;
for(int i = 0; i < 8; ++i) {
results.emplace_back(
pool.enqueue([i] {
std::cout << "hello " << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "world " << i << std::endl;
return i*i;
})
);
}
for(auto && result: results)
std::cout << result.get() << ' ';
std::cout << std::endl;
因为在我的代码中enqueue的函数是c++的成员函数,如果用拉姆达表达式,当时就是不知道怎么写这个代码,最后看到https://github.com/lzpong/threadpool/blob/master/threadpool.h上的注释,使用std::mem_fn或者std::bind才会用了, git地址:https://github.com/lzpong/threadpool)的readme,多看看能帮助理解。
// 提交一个任务
// 调用.get()获取返回值会等待任务执行完,获取返回值
// 有两种方法可以实现调用类成员,
// 一种是使用 bind: .commit(std::bind(&Dog::sayHello, &dog));
// 一种是用 mem_fn: .commit(std::mem_fn(&Dog::sayHello), this)
template<class F, class... Args>
auto commit(F&& f, Args&&... args) ->future<decltype(f(args...))>
{
if (!_run) // stoped ??
throw runtime_error("commit on ThreadPool is stopped.");
using RetType = decltype(f(args...)); // typename std::result_of<F(Args...)>::type, 函数 f 的返回值类型
auto task = make_shared<packaged_task<RetType()>>(
bind(forward<F>(f), forward<Args>(args)...)
); // 把函数入口及参数,打包(绑定)
future<RetType> future = task->get_future();
{ // 添加任务到队列
lock_guard<mutex> lock{ _lock };//对当前块的语句加锁 lock_guard 是 mutex 的 stack 封装类,构造的时候 lock(),析构的时候 unlock()
_tasks.emplace([task](){ // push(Task{...}) 放到队列后面
(*task)();
});
}
#ifdef THREADPOOL_AUTO_GROW
if (_idlThrNum < 1 && _pool.size() < THREADPOOL_MAX_NUM)
addThread(1);
#endif // !THREADPOOL_AUTO_GROW
_task_cv.notify_one(); // 唤醒一个线程执行
return future;
}
前面的例子中的代码,用的是Lambda的不完整写法: [capture list] {function body}
[i] {
std::cout << "hello " << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "world " << i << std::endl;
return i*i;
}
lamda表达式
这个例子里面用了一个lamda表达式,并不是说在调用的enqueue函数的时候,必须https://github.com/lzpong/threadpool用lambda表达式,开始我走入了个误区,lambda表达式写不好,总是不对,学习了Lambda表达时候才写对了,最后代码是这样写的:
初始化一个6个线程的线程池,用来处理N多data,这样一次就可以处理6份数据,处理完放到readyData队列,processTask是Clip的成员函数,可以用Lambda表达式,也可以用std::mem_fun或者std::bind。
threadPool.reset(new ThreadPool(6));
threadPool->init();
for (int idx = 0; idx < data.size(); idx++) {
if (readyData[idx].empty()) {
threadPool->submit([this, idx] {
this->processTask(idx);
});
}
}
用std::mem_fn的写法
threadPool.reset(new ThreadPool(6));
threadPool->init();
for (int idx = 0; idx < data.size(); idx++) {
if (readyData[idx].empty()) {
threadPool->submit(std::mem_fn(&Clip::processTask), this, idx);
}
}
用std::bind的写法
maskThreadPool.reset(new ThreadPool(6));
maskThreadPool->init();
for (int idx = 0; idx < data.size(); idx++) {
if (readyData[idx].empty()) {
threadPool->submit(std::bind(&Clip::processTask, this, idx));
}
}
这里可以看出std::bind和std::mem_fn的区别,std::mem_fn在外面跟了this指针和其他参数,std::bind中跟了所有参数。
用lambda表达式
threadPool.reset(new ThreadPool(6));
threadPool->init();
for (int idx = 0; idx < data.size(); idx++) {
if (readyData[idx].empty()) {
threadPool->submit([this, idx] {
this->processTask(idx);
});
}
}
Lambda 表达式
简单来说,编程中提到的 lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。这一用法跟所谓 λ 演算(题目说明里的维基链接)的关系,有点像原子弹和质能方程的关系,差别其实还是挺大的。
Lambda表达式完整的声明格式如下:
[capture list] (params list) mutable exception-> return type { function body }
各项具体含义如下
- capture list:捕获外部变量列表
- params list:形参列表
- mutable指示符:用来说用是否可以修改捕获的变量
- exception:异常设定
- return type:返回类型
- function body:函数体
此外,我们还可以省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有以下几种:
序号 | 格式 |
1 | [capture list] (params list) -> return type {function body} |
2 | [capture list] (params list) {function body} |
3 | [capture list] {function body} |
std::bind绑定普通函数
- bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。因此std::bind (my_divide, x, y)等价于std::bind (&my_divide,x, y);
double my_divide (double x, double y) {return x/y;}
auto fn_half = std::bind (my_divide, x, y);
std::bind绑定一个成员函数
- bind绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。
class Foo {
void print_sum(int n1, int n2) {
std::cout << n1+n2 << '\n';
}
int data = 10;
};
int main() {
Foo foo;
auto f = std::bind(&Foo::print_sum, &foo, 95, 5);
f(5, 95); // 100
}