Linux多线程编程工具库liblmp_tool github: https://github.com/Dwyane05/liblmp_tool
使用条件变量和互斥锁实现线程池:
头文件:
/*
* Use of this source code is governed by a BSD-style license
* that can be found in the License file.
*
* ThreadPool.h
* Created on: May 30, 2019
* Author: cuiyongfei
*/
#ifndef LMP_TOOL_THREADPOOL_H
#define LMP_TOOL_THREADPOOL_H
#include "Condition.h"
#include "Mutex.h"
#include "Thread.h"
#include "Common.h"
#include
#include
namespace lmp_tool
{
class ThreadPool : noncopyable
{
public:
typedef std::function
explicit ThreadPool(const string& nameArg = string("ThreadPool"));
~ThreadPool();
// Must be called before start().
void setMaxQueueSize(int maxSize) { maxQueueSize_ = maxSize; } //设置队列大小
void setThreadInitCallback(const Task& cb) //回调任务
{ threadInitCallback_ = cb; }
void start(int numThreads); //启动 参数是线程数
void stop(); //停止
const string& name() const
{ return name_; }
size_t queueSize() const;
// Could block if maxQueueSize > 0
// There is no move-only version of std::function in C++ as of C++14.
// So we don't need to overload a const& and an && versions
// as we do in (Bounded)BlockingQueue.
// https://stackoverflow.com/a/25408989
void run(Task f);
private:
bool isFull() const REQUIRES(mutex_); //线程池man??
void runInThread();
Task take(); //取任务
mutable MutexLock mutex_;
Condition notEmpty_ GUARDED_BY(mutex_);
Condition notFull_ GUARDED_BY(mutex_);
string name_;
Task threadInitCallback_;
std::vector
std::deque
size_t maxQueueSize_;
bool running_;
};
} // namespace lmp_tool
#endif // LMP_TOOL_THREADPOOL_H
.cc文件
/*
* Use of this source code is governed by a BSD-style license
* that can be found in the License file.
*
* ThreadPool.cc
* Created on: May 30, 2019
* Author: cuiyongfei
*/
#include "ThreadPool.h"
#include "Exception.h"
#include
#include
using namespace lmp_tool;
ThreadPool::ThreadPool(const string& nameArg)
: mutex_(),
notEmpty_(mutex_),
notFull_(mutex_),
name_(nameArg),
maxQueueSize_(0),
running_(false)
{
}
ThreadPool::~ThreadPool()
{
if (running_)
{
stop();
}
}
void ThreadPool::start(int numThreads)
{
assert(threads_.empty());
running_ = true;
threads_.reserve(numThreads);
for (int i = 0; i < numThreads; ++i)
{
char id[32];
snprintf(id, sizeof id, "%d", i+1);
threads_.emplace_back(new lmp_tool::Thread(
std::bind(&ThreadPool::runInThread, this), name_+id));
threads_[i]->start();
}
if (numThreads == 0 && threadInitCallback_)
{
threadInitCallback_();
}
}
void ThreadPool::stop()
{
{
MutexLockGuard lock(mutex_);
running_ = false;
notEmpty_.notifyAll();
}
for (auto& thr : threads_)
{
thr->join();
}
}
size_t ThreadPool::queueSize() const
{
MutexLockGuard lock(mutex_);
return queue_.size();
}
void ThreadPool::run(Task task)
{
if (threads_.empty())
{
task();
}
else
{
MutexLockGuard lock(mutex_);
while (isFull())
{
notFull_.wait();
}
assert(!isFull());
queue_.push_back(std::move(task));
notEmpty_.notify();
}
}
ThreadPool::Task ThreadPool::take()
{
MutexLockGuard lock(mutex_);
// always use a while-loop, due to spurious wakeup
while (queue_.empty() && running_)
{
notEmpty_.wait();
}
Task task;
if (!queue_.empty())
{
task = queue_.front();
queue_.pop_front();
if (maxQueueSize_ > 0)
{
notFull_.notify();
}
}
return task;
}
bool ThreadPool::isFull() const
{
mutex_.assertLocked();
return maxQueueSize_ > 0 && queue_.size() >= maxQueueSize_;
}
void ThreadPool::runInThread()
{
try
{
if (threadInitCallback_)
{
threadInitCallback_();
}
while (running_)
{
Task task(take());
if (task)
{
task();
}
}
}
catch (const Exception& ex)
{
fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str());
fprintf(stderr, "reason: %s\n", ex.what());
fprintf(stderr, "stack trace: %s\n", ex.stackTrace());
abort();
}
catch (const std::exception& ex)
{
fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str());
fprintf(stderr, "reason: %s\n", ex.what());
abort();
}
catch (...)
{
fprintf(stderr, "unknown exception caught in ThreadPool %s\n", name_.c_str());
throw; // rethrow
}
}
测试代码:
#include "ThreadPool.h"
#include "CountDownLatch.h"
#include "CurrentThread.h"
#include "Logging.h"
#include
#include
void print()
{
printf("tid=%d\n", lmp_tool::CurrentThread::tid());
}
void printString(const std::string& str)
{
LOG_INFO << str;
usleep(100*1000);
}
void test(int maxSize)
{
LOG_WARN << "Test ThreadPool with max queue size = " << maxSize;
lmp_tool::ThreadPool pool("MainThreadPool");
pool.setMaxQueueSize(maxSize);
pool.start(5);
LOG_WARN << "Adding";
pool.run(print);
pool.run(print);
for (int i = 0; i < 100; ++i)
{
char buf[32];
snprintf(buf, sizeof buf, "task %d", i);
pool.run(std::bind(printString, std::string(buf)));
}
LOG_WARN << "Done";
lmp_tool::CountDownLatch latch(1);
pool.run(std::bind(&lmp_tool::CountDownLatch::countDown, &latch));
latch.wait();
pool.stop();
}
/*
* Wish we could do this in the future.
void testMove()
{
lmp_tool::ThreadPool pool;
pool.start(2);
std::unique_ptr
pool.run([y = std::move(x)]{ printf("%d: %d\n", lmp_tool::CurrentThread::tid(), *y); });
pool.stop();
}
*/
int main()
{
test(0);
test(1);
test(5);
test(10);
test(50);
}