Thread factory of the nine Yin scriptures of C + +

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;
};

Posted on Wed, 03 Jun 2020 12:04:49 -0400 by aeboi80