直接上代码如下:
#ifndef THREADS_CONTAINER_H
#define THREADS_CONTAINER_H
#include <queue>
#include <atomic>
#include <future>
#include <thread>
#include <functional>
#include <stdexcept>
class ThreadsContainer
{
using Task = std::function<void()>;
public:
ThreadsContainer(int size = std::thread::hardware_concurrency() / 2){
CreateTasks(size);
}
~ThreadsContainer(){
isRun = false;
condition.notify_all();
for (std::thread& thread : container) {
if (thread.joinable()){
thread.join();
}
}
}
ThreadsContainer(const ThreadsContainer &) = delete;
ThreadsContainer &operator=(const ThreadsContainer &) = delete;
template<class F, class... Args>
auto Start(F&& f, Args&&... args)
-> std::future<decltype(std::forward<F>(f)(std::forward<Args>(args)...))> {
using ReturnType = decltype(std::forward<F>(f)(std::forward<Args>(args)...));
auto task = std::make_shared<std::packaged_task<ReturnType()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...));
{
std::unique_lock<std::mutex> lock{taskLock};
if (!isRun) {
exit(1);
}
allTasks.emplace([task] { (*task)(); });
}
condition.notify_one();
return task->get_future();
}
private:
void CreateTasks(unsigned int size)
{
for (; size > 0; --size)
{
container.emplace_back( [this]{
while (true)
{
Task task;
{
std::unique_lock<std::mutex> lock{ taskLock };
condition.wait(lock, [this] {
return !isRun || !allTasks.empty();
});
if (!isRun && allTasks.empty()){
return;
}
task = std::move(allTasks.front());
allTasks.pop();
}
task();
}
});
}
}
std::vector<std::thread> container;
std::mutex taskLock;
std::condition_variable condition;
std::atomic<bool> isRun{ true };
std::queue<Task> allTasks;
};
#endif
知识点说明:
- 该线程池完全是用C++11并发库实现的。
- std::future的用法,参见C++11多线程之future和promise
- std::packaged_task的用法,参见c++11多线程之packaged_task<>介绍与实例。
- std::function用法,参见C++11中的std::function
- std::thread用法,参见:《C++11线程管理基础》、《c++11 thread类的简单使用》、《c++11仔细地将参数传递给线程std::thread》、《C++11向线程函数传递参数》、《C++11多线程thread参数传递问题》、《C++11中线程所有权转移分析》
- std::atomic用法,参见std::atomic
-
std::condition_variable用法,参见std::condition_variable
- std::unique_lock用法,参见std::unique_lock
-
std::mutex用法,参见std::mutex
-
container.emplace_back不能改为container.push_back,否则编译报错。emplace_back和push_back的区别,参见c++11 之emplace_back 与 push_back的区别
-
Start函数是模板函数,用到了可变参数。
-
Start函数的第一个参数是线程执行函数,该线程执行函数必须有返回值,返回值不能是void类型。
-
该线程池需细品,在此不详述,能看懂,说明你对C++11并发库完全掌握了。
调用方法示例:
1.普通无参函数作为线程函数
int doWork() // 线程执行函数必须有返回值
{
return 1;
}
int main(int argc, char** argv)
{
ThreadsContainer threadsConter;
auto retThreadReturnValue = threadsConter.Start(doWork);
auto retValue = retThreadReturnValue.get(); // 获取线程函数的返回结果
}
2.普通有参函数作为线程函数
int doWork(int param) // 线程执行函数必须有返回值
{
std::cout << param;
return 1;
}
int main(int argc, char** argv)
{
ThreadsContainer threadsConter;
auto retThreadReturnValue = threadsConter.Start(doWork, 100); // 这里可以根据自己的业务需求,传入多个参数
auto retValue = retThreadReturnValue.get();
}
2.类的静态无参成员函数作为线程函数
class My
{
public:
static int doWork_static() // 线程执行函数必须有返回值
{
return 1;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThreadsContainer threadsConter;
threadsConter.Start(&My::doWork_static); // 类的静态无参数成员函数作为线程函数
auto retValue = retThreadReturnValue.get();
...... // 其它代码略
}
3.类的静态有参成员函数作为线程函数
class My
{
public:
static int doWork_static_hasParam(int a) // 线程执行函数必须有返回值
{
return 1;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThreadsContainer threadsConter;
threadsConter.Start(&My::doWork_static_hasParam, 10); // 类的静态无参数成员函数作为线程函数
auto retValue = retThreadReturnValue.get();
...... // 其它代码略
}
3.类的无参成员函数作为线程函数
class My
{
public:
int doWork() // 线程执行函数必须有返回值
{
return 1;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThreadsContainer threadsConter;
My h;
auto f = std::bind(&My::doWork, &h);
auto retThreadReturnValue = threadsConter.Start(f);
auto retValue = retThreadReturnValue.get();
...... // 其它代码略
}
4.类的有参成员函数作为线程函数
class My
{
public:
int doWork_hasParam(int a) // 线程执行函数必须有返回值
{
return 1;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThreadsContainer threadsConter;
My h;
auto f = std::bind(&My::doWork_hasParam, &h, std::placeholders::_1);
auto retThreadReturnValue = threadsConter.Start(f, 96);
auto retValue = retThreadReturnValue.get();
...... // 其它代码略
}
还可以用lmba表达式、函数对象作为线程函数,在此不再举例。