The first part mainly introduces the use scenario of thread factory, and this part mainly introduces the implementation of thread factory.
The thread factory consists of two parts:
First, factory class, which is mainly responsible for creating thread class and receiving and extracting data;
Second, it is the thread interface class, which mainly defines the thread entry class and the public interface to receive messages;
According to the message model, it can be divided into data-based factory class and task-based factory class.
Factory class definition:
class CThreadFactoryBase { public: virtual ~CThreadFactoryBase() {}; virtual bool IsStop() = 0; virtual bool Pop(AnyVar& val, int waitTime = -1) = 0; }; //task model struct TaskModel {}; //data model struct DataModel {}; template<typename T, typename P> class __ThreadFactory;
Task model factory class implementation
template<typename T> class __ThreadFactory<T, typename std::enable_if<std::is_same<typename T::ModelType, TaskModel>::value, TaskModel>::type> : public ThreadObject, public CThreadFactoryBase { using ValType = typename T::ValueType; using ValuePtrType = typename T::ValuePtrType; using RetType = typename T::RetType; using RecvType = typename T::RecvType; public: //Pressing a task into a queue auto Push(typename T::RecvType val) ->std::future<RetType> { ValuePtrType task = std::make_shared<ValType>(val); std::future<RetType> future = task->get_future(); { std::unique_lock<std::mutex> lock(m_mu); //Submitting jobs to a stopped thread pool is not allowed if (m_bStop) throw std::runtime_error("Submit a job to a stopped thread factory"); while (m_taskQueue.size() == m_nCapacity) //Queue full { m_condPush.wait(m_mu); //Wait, will unlock temporarily } m_taskQueue.push(task); } m_condPop.notify_one(); // Wake up a thread to execute return future; } protected: std::queue<ValuePtrType> m_taskQueue; //queue };
Data model factory class implementation
template <typename T, typename RET> struct DataNode { T data; std::promise<RET> res; DataNode(const T& d, std::promise<RET>& prs) :data(d), res(std::move(prs)) {} }; template<typename T> class __ThreadFactory<T, typename std::enable_if<std::is_same<typename T::ModelType, DataModel>::value, DataModel>::type> : public ThreadObject, public CThreadFactoryBase { using ValType = typename T::ValueType; using ValuePtrType = typename T::ValuePtrType; using RetType = typename T::RetType; using RecvType = typename T::RecvType; public: //Pressing a task into a queue std::future<RetType> Push(const RecvType& val) { std::promise<RetType> prs; auto future = prs.get_future(); ValuePtrType tupVal = std::make_shared<ValType>( val, prs ); { std::unique_lock<std::mutex> lock(m_mu); //Submitting jobs to a stopped thread pool is not allowed if (m_bStop) throw std::runtime_error("Submit a job to a stopped thread factory"); while (m_taskQueue.size() == m_nCapacity) //Queue full { m_condPush.wait(m_mu); //Wait, will unlock temporarily } m_taskQueue.push(tupVal); } m_condPop.notify_one(); // Wake up a thread to execute return future; } protected: std::queue<ValuePtrType> m_taskQueue; //queue };
Factory class implementation, according to the thread interface class model, automatic identification
template<typename T> class CThreadFactory: public __ThreadFactory<T, typename T::ModelType> { public: ~CThreadFactory() { Stop(); for (std::thread& thread : m_pThreadPool) { if (thread.joinable()) thread.join(); // Wait for the task to finish, provided that the thread will finish executing } m_pThreadPool.clear(); } template<typename ...P> void Start(unsigned short nThreadNum = 1, P&&... p) { for (size_t i = 0; i < nThreadNum; i++) { m_pThreadPool.emplace_back([this, p...]() { T obj(*this, p...); obj.Run(); }); } } //Get a task from the task queue //waitTime: is the waiting time in seconds bool Pop(AnyVar& task, int waitTime = -1) { m_idlThrNum++; { std::unique_lock<std::mutex> lock(m_mu); if (waitTime < 0) { this->m_condPop.wait(lock, [this] { return this->m_bStop.load() || !this->m_taskQueue.empty(); }); // wait until there is a task } else { auto status = m_condPop.wait_for(lock, std::chrono::seconds(waitTime), [this] { return this->m_bStop.load() || !this->m_taskQueue.empty(); }); if (!status) { m_idlThrNum--; return false; } } if (this->m_taskQueue.empty()) { if (this->m_bStop) { m_idlThrNum--; } return false; } task = std::move(this->m_taskQueue.front()); // Take a task this->m_taskQueue.pop(); } //Notification writer thread m_condPush.notify_one(); m_idlThrNum--; return true; } virtual size_t GetTaskNum() { return m_taskQueue.size(); } virtual bool IsStop() { return ThreadObject::IsStop(); } };
Thread interface class implementation
//Task based thread interface template<typename RET, typename... Args> class BaseOnTask { public: using ModelType = TaskModel; using ValueType = std::packaged_task<RET(Args ...)>; using ValuePtrType = std::shared_ptr<std::packaged_task<RET(Args ...)>>; using RecvType = std::function<RET(Args ...)>; using RetType = RET; }; template<typename RET, typename... Args> class BaseOnData { public: using ModelType = DataModel; using ValueType = DataNode<std::tuple<Args ...>, RET>; using ValuePtrType = std::shared_ptr<DataNode<std::tuple<Args ...>, RET>>; using RecvType = std::tuple<Args...>; using RetType = RET; }; //Task thread interface template<typename T> class ThreadJob : public T { public: using ValType = typename T::ValueType; using ValuePtrType = typename T::ValuePtrType; using RetType = typename T::RetType; using ModelType = typename T::ModelType; using RecvType = typename T::RecvType; ThreadJob(CThreadFactoryBase& factory) :m_factory(factory) {} bool GetJob(ValuePtrType& task, int waitTime = -1) { AnyVar val; if (m_factory.Pop(val, waitTime)) { task = any_cast<ValuePtrType>(val); return true; } return false; } bool IsStop() { return m_factory.IsStop(); } //Thread start function virtual void Run() = 0; private: CThreadFactoryBase& m_factory; }; template<typename RET, typename... Args> class BaseTaskThread : public ThreadJob< BaseOnTask<RET, Args...>> { public: using ThreadJob< BaseOnTask<RET, Args...>>::ThreadJob; }; template<typename RET, typename... Args> class BaseDataThread : public ThreadJob< BaseOnData<RET, Args...>> { public: using ThreadJob< BaseOnData<RET, Args...>>::ThreadJob; };