C++11 ThreadPool总结

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
}

 

参考:

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值