为什么Threadpool不加速而是减速很多



我在github中读到了一些线程池的代码。

这是一个很好的,由我修改的。

#pragma once
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
class ThreadPool {
public:
ThreadPool(size_t);
template<class F, class... Args>
auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;  // -> apparently define return type is future
~ThreadPool();
private:
// need to keep track of threads so we can join them
std::vector< std::thread > workers;
// the task queue
std::queue< std::function<void()> > tasks;

// synchronization
std::mutex queue_mutex;
std::condition_variable condition;
bool stop;
};

// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads) : stop(false) {
for(size_t i = 0;i<threads;++i)
workers.emplace_back(  // thread variable, emplace back just pass in a function for thread to construct
[this] {
for(;;) {
std::function<void()> task;
{   
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); 
// wait stop true or task not empty
if(this->stop && this->tasks.empty()) return;  // only on stop and empty task, perfect exit
// if only stop, still will hanle left task
task = std::move(this->tasks.front());  // get the first task
this->tasks.pop();
}   
task();
}   
}); 
}
// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared< std::packaged_task<return_type()> >(std::bind(std::forward<F>(f), std::forward<Args>(args)...));     
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
// don't allow enqueueing after stopping the pool
if(stop) throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace([task](){ (*task)(); });
}
condition.notify_one();
return res;
}
// the destructor joins all threads
inline ThreadPool::~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for(std::thread &worker: workers) worker.join();
}

我用它来加速我的代码。例如,我有1000个独立的任务,每个任务的成本约为1。

我发现使用这个线程池,它比一个循环慢。

所以,我写了一些小代码来检查。

这是我的密码。

#include "./time_util.h" // this is a timer class, just use to get time
#include "./thread_pool.hpp"  //  this is the class Threadpool header
size_t i = 0;
void test() {
// printf("hahn");
i++;
}
#define N 1000000
void fun1() {
printf("func1n");
ThreadPool pool(10);
for (int i = 0; i < N; ++i) {
pool.enqueue(test);
}
}
void fun2() {
printf("func2n");
for (int i = 0; i < N; ++i) {
test();
}
}
int main(int argc, char** argv) {
util::time_util::Timer t;
t.StartTimer();
if (argc == 1) {
fun1();
} else {
fun2();
}
printf("i=%dn", i); 
t.EndTimer("1");
}

循环版本的成本为0.005秒线程池花费5秒。

我机器的cpu是2.2G 24核(48线程(,centos7。

并且,线程池的结果是不正确的(这没关系,因为我没有添加锁(

你能解释一下吗?这是否意味着对于小任务,我不能使用线程池来加快速度?

线程池是一个非常"昂贵的";公用事业如果您有多个繁重的计算任务,那么线程池引入的开销可以忽略不计,这是有意义的。

在您的情况下,i++的计算非常琐碎,您基本上只是测量线程池的开销。试着用一个需要更长时间的计算来替换它,例如用sleep(1)来查看差异。

附带说明:size_t i;i++不是线程安全的,您可能会得到错误的结果。请改用atomic<size_t> i;


注意:线程池对于一些任务很有用,比如在Web服务器中处理工作者,而对于其他任务则不太有用,比如实现排序算法或求和数字。如果你想并行化计算,C++17为许多标准算法带来了并行执行策略,在这些算法中,所有线程处理都由你负责。请参阅此处https://en.cppreference.com/w/cpp/algorithm这里是C++中的并行循环

最后,永远不要低估你在现代CPU上可以从单个内核中挤出的绝对功率。

最新更新