Thread pool of C + + nine Yin scriptures

Role of thread pool:

1. Avoid the cost of creating and destroying threads when processing short-term tasks;

2. In a large number of concurrent tasks, system resources can be used more reasonably to cut peak and level Valley to ensure the stability of system operation;

It has the following advantages to build thread pool based on C++11 standard;

1. Task does not need to inherit interface

2. Support lambda expression

3. Support global function and static member function;

4. Use bind to support member functions;

//Thread pool
class CThreadPool : public ThreadObject
{
public:
	using ThreadObject::ThreadObject;
	//Start default thread pool
	void Start(int nThreadNum = 1)
	{
		m_idlThrNum = nThreadNum < 1 ? 1 : nThreadNum;
		for (auto size = 0; size < nThreadNum; ++size)
		{   //Number of initialization threads
			m_pThreadPool.emplace_back(
				[this]
			{ // Worker functions
				while (!this->m_bStop)
				{
					std::function<void(void)> task;
					{   // Get a task to be executed
						// unique_lock vs lock_ The advantage of guard is that it can unlock() and lock() at any time
						std::unique_lock<std::mutex> lock(m_mu);

						this->m_condPop.wait(lock,
							[this] {
							return this->m_bStop.load() || !this->m_taskQueue.empty();
						}
						); // wait until there is a task
						if (this->m_bStop && this->m_taskQueue.empty())
							return;
						task = std::move(this->m_taskQueue.front()); // Take a task
						this->m_taskQueue.pop();
					}
					//Notification writer thread
					m_condPush.notify_one();
					m_idlThrNum--;
					task();
					m_idlThrNum++;
				}
			}
			);
		}
	}
   
   //Submit task to thread pool
	template<class F, class... Args>
	auto Commit(F&& f, Args&&... args) -> std::future<decltype(f(args...))>
	{
		using return_type = decltype(f(args...));

		auto task = std::make_shared< std::packaged_task<return_type()> >(
			std::bind(std::forward<F>(f), std::forward<Args>(args)...)
			);

		std::future<return_type> res = 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 jobs to a stopped thread pool");

			while (m_taskQueue.size() == m_nCapacity)         //Queue full
			{
				m_condPush.wait(m_mu);                         //Wait, will unlock temporarily
			}

			m_taskQueue.emplace([task]() { (*task)(); });
		}
		m_condPop.notify_one();
		return res;
	}

	virtual size_t GetTaskNum()
	{
		return m_taskQueue.size();
	}
   
private:
	std::queue<std::function<void()>> m_taskQueue;                    //queue
};

Test code:

void Func1(int i, const std::string& msg)
{
	std::cout << i << "-->" << msg<< std::endl;
}

int Func2(int i, const std::string& msg)
{
	std::cout << i << "-->" << msg << std::endl;
	std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	return i * 100;
}

class PrintTest {
public:
	void Func1(int i, const std::string& msg)
	{
		std::cout << i << "-->" << msg << std::endl;
	}

	void Func2(int i)
	{
		std::cout << i << "-->" << "Test member functions" << std::endl;
	}
};

int main()
{
    CThreadPool pool;
	pool.Start(2);
	//Test lambda expressions
	pool.Commit([] {std::cout << "test lambda expression" << std::endl; });
	//Test lambda expression with parameters
	pool.Commit([](int val){std::cout << "Test with parameters lambda expression" << "-->" << val << std::endl; }, 999);
	//Test global functions
	pool.Commit(Func1, 100, "Test global functions");
	//Test member functions
	PrintTest p;
	pool.Commit(std::mem_fn(&PrintTest::Func1),&p , 200, "Test member functions");

	//Test synchronization get results
	auto res = pool.Commit(Func2, 300, "Test synchronization get results");
	auto val = res.get();
	std::cout << "Get results:" << val << std::endl;

	std::this_thread::sleep_for(std::chrono::milliseconds(5000));

    return 0;
}

Tags: Programming Lambda

Posted on Mon, 01 Jun 2020 11:39:53 -0400 by scuba