由于最近在学习
muduo
网络库,需要许多的多线程和网络编程的知识。现在正好看到线程池的部分,就想着用C++11
来编写一个线程池来加深自己的对线程池的理解。中间要是有什么不好或则不对的地方,希望各位大神指出。
thread
的介绍thread
中的重要函数void join(); // 阻塞当前线程直至所标识的线程结束其执行
bool joinable() ;// 检查当前线程是否是可以join()的,一般join()配合使用
void detach(); // 从 thread 对象分离执行线程,允许执行独立地持续。一旦该线程退出,则释放任何分配的资源.
// msvs 的thread类的源码
// 其他的编译 可能实现了但是作用相同都是赋值后,之前的就不可以用了
thread(const thread&) = delete;
thread& operator=(const thread&) = delete;
thread
是不可以赋值和拷贝构造的thread
可以std::move
的,std::move
的作用是将左值引用转换为右值引用,之后就可以调用移动拷贝构造或则移动赋值运算符,将当前的线程权限转交给另外一个线程,转交后之前的线程对象就不可以用了#include
#include //添加头文件
void print()
{
......
}
int main()
{
std::thread td1(print);
std::cout << "before td1: " << td1.joinable() << std::endl; // before td1: 1
// std::thread td2 = td1; // 报错
std::thread td2 = std::move(td1);
std::cout << "after td1: " << td1.joinable() << std::endl; // before td1: 0
std::cout << "td2: " << td2.joinable() << std::endl; //td2: 1
if (td2.joinable())td2.join();
}
condition_variable
条件变量的介绍// 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,循环直至满足某谓词。
//pred 这个一般为lambda函数, 当pred返回的值为true并且wait唤醒时可以向下执行
void wait( std::unique_lock<std::mutex>& lock, Predicate pred);
void notify_one(); // 若任何线程在此变量中等待,则调用 notify_one 会解除阻塞等待线程之一
void notify_all(); // 解除阻塞全部当前等待于此变量的线程
注意:
condition_variable
被通知,等待超时或虚假唤醒发生时,等待于该条件变量上的线程将会被唤醒并重新获得该互质锁。之后线程会检查条件,若唤醒是虚假的,则继续等待。notify_one()/notify_all()
不必保有锁packaged_task
的介绍std::future<T> get_future(); // 得到于 packaged_task对象所关联的future对象,get_future 只能对每个packaged_task调用一次,调用第二次会崩溃
说明:
packaged_task
是对调用对象的包装,如函数 、lambda 表达式、 bind 表达式等使得能异步调用它packaged_task
的返回值或所抛异常被存储于能通过 std::future 对象访问的共享状态中#include
#include
#include
#include
#include
int f(int x, int y) { return std::pow(x,y); }
std::future<int> taskThread()
{
std::packaged_task<int(int,int)> task(f);
std::future<int> result = task.get_future();
std::thread task_td(std::move(task), 2, 10);
task_td.join();
return result;
}
int main()
{
auto result = taskThread();
std::cout << "task_thread:\t" << result.get() << '\n'; // task_thread: 1024
}
// Threadpool.h
#pragma once
#ifndef THREADPOOL
#define THREADPOOL
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define HAS_RETURN
using Task = std::function<void()>;
class Threadpool
{
public:
Threadpool();
~Threadpool();
Threadpool(const Threadpool&) = delete;
Threadpool& operator=(const Threadpool&) = delete;
void start(size_t num = 5);
#ifndef HAS_RETURN
void addThread(Task task);
#else
template<class Fun, class... Args>
auto addThread(Fun&& fun, Args&&... args)->std::future<decltype(fun(args...))>
{
assert(!threads_.empty());
assert(run_.load());
using RType = decltype(fun(args ...));
auto task = std::make_shared<std::packaged_task<RType()>>(
std::bind(std::forward<Fun>(fun), std::forward<Args>(args)...));
std::future<RType> rValue = task->get_future();
{
std::unique_lock<std::mutex> guard{ mutex_ };
isFull_.wait(guard, [this]() {return tasks_.size() < taskMaxSize; });
tasks_.emplace(
[task]
{
(*task)();
});
}
isEmpty_.notify_one();
return rValue;
}
#endif
void setTaskMaxSize(int value); // 开始start的时候调用
int getCurTaskSize();
private:
void runInThread(); // 线程的入口函数
private:
std::queue<Task> tasks_; // 存放处理任务
std::vector<std::unique_ptr<std::thread>> threads_; // 存放线程容器
std::atomic<bool> run_; // 是否正在运行 原子操作
size_t taskMaxSize; // 设置存放任务的最大数量
std::mutex mutex_; // 互斥锁
std::condition_variable isEmpty_; // 条件变量 当任务数目为空的时候等待
std::condition_variable isFull_; // 条件变量 当任务数目为满的时候等待
};
#endif
// Threadpool.cpp
#include "Threadpool.h"
#include
#include
Threadpool::Threadpool():
run_(false),
taskMaxSize(5)
{
}
Threadpool::~Threadpool()
{
run_.store(false);
isEmpty_.notify_all();
for (auto& td : threads_)
{
if (td->joinable())
{
td->join(); //等待所有线程结束
}
}
}
void Threadpool::start(size_t num)
{
assert(threads_.empty());
run_.store(true);
for (size_t i = 0; i < num; i++)
{
threads_.emplace_back(new std::thread(&Threadpool::runInThread, this));
}
}
#ifndef HAS_RETURN
void Threadpool::addThread(Task task)
{
assert(!threads_.empty());
if (!task) return;
{
std::unique_lock<std::mutex> guard{ mutex_ };
isFull_.wait(guard, [this]() {return tasks_.size() < taskMaxSize;});
tasks_.push(std::move(task));
}
isEmpty_.notify_one();
}
#endif // HAS_RETURN
int Threadpool::getCurTaskSize()
{
std::lock_guard<std::mutex> guard(mutex_);
return tasks_.size();
}
void Threadpool::setTaskMaxSize(int value)
{
if (value <= 0) return ;
taskMaxSize = value;
}
void Threadpool::runInThread()
{
while (run_)
{
Task task;
{
std::unique_lock<std::mutex> guard{ mutex_ };
isEmpty_.wait(guard,
[this](){
return !run_ || !this->tasks_.empty(); // 加入 !run 判断是因为当 Threadpool 可以正常释放释放时
});
if (!run_) return; // 这个代表当停止时,后面的任务不做了
task = std::move(tasks_.front());
tasks_.pop();
}
isFull_.notify_one();
task();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
// main.cpp
#include "Threadpool.h"
#include
#include
#include
std::mutex m;
int print(int sum)
{
int i;
for (i = 0; i < sum; i++)
{
m.lock();
std::cout << "threadId:" << std::this_thread::get_id() << " i =" << i << std::endl;
m.unlock();
}
return i;
}
int main()
{
{
Threadpool pool;
pool.setTaskMaxSize(2);
pool.start(2);
#ifdef HAS_RETURN
auto a = pool.addThread(print, 1000);
//pool.addThread(print);
auto b = pool.addThread(print, 1000);
auto c = pool.addThread(print, 1000);
auto d = pool.addThread(print, 1000);
std::cout << "a:" << a.get() << std::endl;
std::cout << "b:" << b.get() << std::endl;
std::cout << "c:" << c.get() << std::endl;
std::cout << "d:" << d.get() << std::endl;
#else
pool.addThread(std::bind(print, 100));
pool.addThread(std::bind(print, 100));
#endif // HAS_RETURN
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
return 0;
}
说明:
HAS_RETURN
时可以获取到 加入到线程函数中返回值,这个返回值保存到future的模板中addThread
加上宏和不加上宏参数和返回值是不同的,用法有些许不同setTaskMaxSize
请在创建对象之后和start
之前调用参考:
学习网站