c++11编写线程池

写在前面

由于最近在学习muduo网络库,需要许多的多线程和网络编程的知识。现在正好看到线程池的部分,就想着用C++11来编写一个线程池来加深自己的对线程池的理解。中间要是有什么不好或则不对的地方,希望各位大神指出。


知识要点

  1. 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();
 }

  1. 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(); // 解除阻塞全部当前等待于此变量的线程

注意:

  • 等待wait的线程必须获得和保护共享变量相同的锁
  • 执行wait时,该线程会自动释放互斥锁,并悬挂线程的执行
  • condition_variable 被通知,等待超时或虚假唤醒发生时,等待于该条件变量上的线程将会被唤醒并重新获得该互质锁。之后线程会检查条件,若唤醒是虚假的,则继续等待。
  • 当条件变量进行通知notify_one()/notify_all()不必保有锁
  1. 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之前调用

参考:
学习网站

你可能感兴趣的:(c++,开发语言)