参考《深入学习C++11》
1概述
1.1设计目标
实现一个线程池,启动时开启n个线程。线程池中的线程从同步消息队列中取任务并执行
1.2线程池的好处
处理并发任务时,如果每来一个请求建立一个线程,大量的线程销毁和创建将消耗过多的系统资源
线程池技术可以通过在系统中预先创建一定数量的线程,当任务到达时从线程池中分配一个预先建立的线程去处理任务,线程在处理完任务之后还可以重用,不会销毁,而是等待下次任务到来,这样就避免了大量线程的创建和销毁动作,从而节省了系统资源
对于多核处理器,多个线程会被分配到多个CPU,提高并行处理的效率
每个线程独立阻塞,可以防止主线程阻塞导致主流程阻塞
1.3设计思路
线程池可以分为三层,同步服务层,排队层和异步服务层
同步服务层负责将上层的请求任务放入排队层
异步服务层从排队层取出任务并行的处理
2实现
2.1同步队列
//syncQueue.h
#pragma once
#include<mutex>
#include<condition_variable>
#include<iostream>
#include<list>
using namespace std;
template<class T>
class syncQueue
{
public:
syncQueue(int max_size);
~syncQueue();
void put(T&x);
void put(T&& x);
void take(list<T>& list);
void take(T& t);
void stop();
bool empty();
bool full();
size_t size();
int count();
private:
void add(T&&x);
bool notEmpty() const;
bool notFull() const;
private:
mutex mmutex;
condition_variable cv_not_empty;
condition_variable cv_not_full;
list<T> queue;
int max_size;
bool need_stop;
};
extern mutex printLock;
//syncQueue.cpp
#include "syncQueue.h"
using namespace std;
template<class T>
syncQueue<T>::syncQueue(int max_size) :max_size(max_size), need_stop(0)
{
}
template<class T>
syncQueue<T>::syncQueue::~syncQueue()
{
}
template<class T>
void syncQueue<T>::put(T&x)
{
add(move(x));
}
template<class T>
void syncQueue<T>::put(T && x)
{
add(forward<T>(x));
}
template<class T>
void syncQueue<T>::take(list<T>& list)
{
unique_lock<mutex> locker(mmutex);
cv_not_empty.wait(locker, [this] {return need_stop || notEmpty(); });
if (need_stop)
return;
list = move(queue);
cv_not_full.notify_one();
}
template<class T>
void syncQueue<T>::take(T & t)
{
unique_lock<mutex> locker(mmutex);
cv_not_empty.wait(locker, [this] {return need_stop || notEmpty(); });
if (need_stop)
return;
t = queue.front();
queue.pop_front();
cv_not_full.notify_one();
}
template<class T>
void syncQueue<T>::stop()
{
{
lock_guard<mutex> = locker(mmutex);
need_stop = true;
}
cv_not_empty.notify_all();
cv_not_full.notify_all();
}
template<class T>
bool syncQueue<T>::empty()
{
lock_guard<mutex> locker(mmutex);
return queue.empty();
}
template<class T>
bool syncQueue<T>::full()
{
lock_guard<mutex> locker(mmutex);
return queue.size() >= max_size;
}
template<class T>
size_t syncQueue<T>::size()
{
lock_guard<mutex> locker(mmutex);
return queue.size();
}
template<class T>
int syncQueue<T>::count()
{
return queue.size();
}
template<class T>
void syncQueue<T>::add(T&&x)
{
unique_lock<mutex> locker(mmutex);
cv_not_full.wait(locker, [this] {return need_stop || notFull(); });
if (need_stop)
return;
queue.emplace_back(forward<T>(x));
cv_not_empty.notify_one();
}
template<class T>
bool syncQueue<T>::notEmpty() const
{
bool res = queue.empty();
if (res)
if (res)
{
lock_guard<mutex> locker(printLock);
cout << "Queue is empty now, wait a minute..." << endl;
}
return !res;
}
template<class T>
bool syncQueue<T>::notFull() const
{
bool res = queue.size() >= max_size;
if (res)
{
lock_guard<mutex> locker(printLock);
cout << "Queue is full, wait a minute..." << endl;
}
return !res;
}
同步队列测试代码:
#include <iostream>
#include "syncQueue.cpp"
using namespace std;
int main()
{
auto p = make_shared<syncQueue<int>>(10);
while (1)
{
int tmp;
cout << "input a num:" << endl;
cin >> tmp;
if (tmp)
p->put(tmp);
else
p->take(tmp);
}
system("pause");
return 0;
}
测试结果:
input a num:
1
input a num:
0
input a num:
0
Queue is empty now, wait a minute...
2.2线程池
//threadPool.h
#pragma once
#include "syncQueue.cpp"
#include <thread>
#include <functional>
const int maxTaskNum = 100;
using Task = function<void()>;
class threadPool
{
public:
threadPool(int numThreads = thread::hardware_concurrency());
~threadPool();
void stop();
void addTask(Task&&task);
private:
void start(int numThreads);
void run();
void stopThreadGroup();
syncQueue<Task> queue;
list<shared_ptr<thread>> threadGroup;
once_flag flag;
bool running;
};
extern mutex printLock;
#include "threadPool.h"
using namespace std;
threadPool::threadPool(int numThreads):queue(maxTaskNum)
{
start(numThreads);
}
threadPool::~threadPool()
{
stop();
}
void threadPool::stop()
{
call_once(flag, [this] {stopThreadGroup(); });
}
void threadPool::addTask(Task&&task)
{
queue.put(forward<Task>(task));
}
void threadPool::start(int numThreads)
{
running = true;
for (int i = 0; i < numThreads; i++)
{
threadGroup.emplace_back(make_shared<thread>(&threadPool::run, this));
}
}
void threadPool::run()
{
while (running)
{
list<Task> list;
queue.take(list);
for (auto task : list)
{
if (!running)
return;
{
lock_guard<mutex> locker(printLock);
cout << "thread id used this time is: " << this_thread::get_id() << endl;
}
task();
}
}
}
void threadPool::stopThreadGroup()
{
queue.stop();
running = false;
for (auto thread : threadGroup)
{
if (thread)
thread->join();
}
threadGroup.clear();
}
线程池测试代码:
#include <iostream>
#include <chrono>
#include "threadPool.h"
using namespace std;
mutex printLock;//打印锁,防止打印串行
int main()
{
/*测试同步队列*/
//int queue_size = 0;
//cout << "input queue size:" << endl;
//cin >> queue_size;
//auto p = make_shared<syncQueue<int>>(queue_size);
//while (1)
//{
// int tmp;
// cout << "input a num:" << endl;
// cin >> tmp;
// if (tmp)
// p->put(tmp);
// else
// p->take(tmp);
//}
/*测试线程池*/
threadPool pool;
thread t1([&pool] {
for (int i = 0; i < 5; i++)
{
auto id = this_thread::get_id();
pool.addTask([id] {
this_thread::sleep_for(chrono::milliseconds(100));
lock_guard<mutex> locker(printLock);
cout << "working for thread: " << id << endl; });
}
});
thread t2([&pool] {
for (int i = 0; i < 5; i++)
{
auto id = this_thread::get_id();
pool.addTask([id] {
this_thread::sleep_for(chrono::milliseconds(100));
lock_guard<mutex> locker(printLock);
cout << "working for thread: " << id << endl; });
}
});
this_thread::sleep_for(chrono::seconds(2));
pool.stop();
t1.join();
t2.join();
system("pause");
return 0;
}
测试结果:
Queue is empty now, wait a minute...
Queue is empty now, wait a minute...
Queue is empty now, wait a minute...
Queue is empty now, wait a minute...
Queue is empty now, wait a minute...
thread id used this time is: 16092
thread id used this time is: 17264
Queue is empty now, wait a minute...
thread id used this time is: 11768
Queue is empty now, wait a minute...
working for thread: 6720
thread id used this time is: 16092
working for thread: 6720
thread id used this time is: 17264
working for thread: 4052
thread id used this time is: 11768
working for thread: 6720
thread id used this time is: 16092
working for thread: 6720
thread id used this time is: 17264
working for thread: 4052
Queue is empty now, wait a minute...
working for thread: 4052
thread id used this time is: 16092
working for thread: 6720
Queue is empty now, wait a minute...
working for thread: 4052
thread id used this time is: 16092
working for thread: 4052
Queue is empty now, wait a minute...