背景
常见的线程池实现一般类似多生产者多消费者场景,生产者将任务投递至线程池,而不需要关心这个任务最终在哪个线程上执行,但是在某些特殊场景下,比如某一个对象产生的任务处理需要保证顺序,这样传统的线程池就没有办法满足这种需求,于时考虑实现一种可支持将任务投递至指定线程的线程池。
实现思路
接口如下:
void exec(std::function<void()> &&task, int trdNo = -1)
1.线程池在创建时为每个线程创建一个队列。
2.正常情况下,只需要传入task,这时会随机将task扔到某个线程的队列。
3.当用户传入trdNo时,则投递到对应的线程的队列。
4.如果能保证对应线程的生产者只会在一个线程,可将队列换成无锁队列增加性能
源码实现
#include <vector>
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <random>
class ThreadPool
{
public:
void start(uint16_t trdNum)
{
if (_start)
{
return;
}
_start = true;
_trdNum = trdNum;
for (int i = 0; i < trdNum; ++i)
{
_trds.emplace_back(std::make_unique<std::thread>(std::bind(&ThreadPool::process, this, i)));
_cvs.emplace_back(std::make_unique<std::condition_variable> ());
_mtx.emplace_back(std::make_unique<std::mutex>());
_trdTasks.emplace_back(std::list < std::function<void()>>());
}
}
//如果trdNo为-1 随机扔到哪个线程的队列
void exec(std::function<void()> &&task, int trdNo = -1)
{
if (!_start)
{
return;
}
uint16_t no = 0;
if (-1 == trdNo)
{
no = getTrdNo();
printf("get no:%d \n", no);
}
else
{
no = trdNo % _trdNum;
}
std::lock_guard<std::mutex> lock(*_mtx[no]);
_trdTasks[no].emplace_back(task);
_cvs[no]->notify_one();
}
void stop()
{
_start = false;
for (int i = 0; i < _trdNum; ++i)
{
_cvs[i]->notify_all();
}
for (const auto& trd: _trds)
{
trd->join();
}
}
private:
void process(int trdNo)
{
while (_start)
{
std::function<void()> task = nullptr;
{
std::unique_lock<std::mutex> lock(*_mtx[trdNo]);
while (_trdTasks[trdNo].empty() && _start)
{
_cvs[trdNo]->wait(lock);
}
if (!_trdTasks[trdNo].empty())
{
task = _trdTasks[trdNo].front();
_trdTasks[trdNo].pop_front();
}
}
if (nullptr != task)
{
task();
}
}
}
uint16_t getTrdNo()
{
if (1 == _trdNum)
{
return 0;
}
std::random_device rd; // 用于随机数引擎获得随机种子
std::mt19937 gen(rd()); // 以rd()为种子的标准mersenne_twister_engine
std::uniform_int_distribution<int> distribute(0, _trdNum - 1);
return distribute(gen) % _trdNum;
}
uint16_t _trdNum = 0;
bool _start = false;
std::vector<std::unique_ptr<std::mutex>> _mtx;
std::vector<std::unique_ptr<std::condition_variable>> _cvs;
std::vector<std::unique_ptr<std::thread>> _trds;
std::vector<std::list<std::function<void()>>> _trdTasks;
};