操作系统--线程池(C++)

1、线程池简介

线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,对于计算密集型任务,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。

任务调度以执行线程的常见方法是使用同步队列,称作任务队列。池中的线程等待队列中的任务,并把执行完的任务放入完成队列中。

线程池模式一般分为两种:HS/HA半同步/半异步模式、L/F领导者与跟随者模式。

  • 半同步/半异步模式又称为生产者消费者模式,是比较常见的实现方式,比较简单。分为同步层、队列层、异步层三层。同步层的主线程处理工作任务并存入工作队列,工作线程从工作队列取出任务进行处理,如果工作队列为空,则取不到任务的工作线程进入挂起状态。由于线程间有数据通信,因此不适于大数据量交换的场合。
  • 领导者跟随者模式,在线程池中的线程可处在3种状态之一:领导者leader、追随者follower或工作者processor。任何时刻线程池只有一个领导者线程。事件到达时,领导者线程负责消息分离,并从处于追随者线程中选出一个来当继任领导者,然后将自身设置为工作者状态去处置该事件。处理完毕后工作者线程将自身的状态置为追随者。这一模式实现复杂,但避免了线程间交换任务数据,提高了CPU cache相似性。在ACE(Adaptive Communication Environment)中,提供了领导者跟随者模式实现。

2、线程池的组成

线程池的一般的执行过程如下图所示:
在这里插入图片描述
从上图可以看出,线程池需要具备如下功能:

  • 提交任务
  • 任务缓存(任务队列)
  • 任务调度
  • 任务执行(线程)

3、线程池的工作情况

  • 主程序当前没有任务要执行,线程池中的任务队列为空闲状态。
    此情况下所有工作线程处于空闲的等待状态,任务缓冲队列为空。
    在这里插入图片描述
  • 主程序添加小于等于线程池中线程数量的任务。
    所有工作线程已处在等待状态,主线程开始添加三个任务,添加后通知(notif())唤醒线程池中的线程开始取(take())任务执行. 此时的任务缓冲队列还是空。
    在这里插入图片描述
  • 主程序添加任务数量大于当前线程池中线程数量的任务。
    所有工作线程都在工作中,主线程开始添加第四个任务,添加后发现现在线程池中的线程用完了,于是存入任务缓冲队列。工作线程空闲后主动从任务队列取任务执行.
    在这里插入图片描述
  • 主程序添加任务数量大于当前线程池中线程数量的任务,且任务缓冲队列已满。
    在这里插入图片描述

4、线程池的实现

  • lock.h
#pragma once

#include <windows.h>

namespace lock_summary
{
	class ILock
	{
	public:
		ILock(){}
		virtual ~ILock(){}

	public:
		virtual void lock() = 0;
		virtual void unlock() = 0;
	};

	class mutex_lock : public ILock
	{
	public:
		mutex_lock();
		~mutex_lock();

	public:
		void lock();
		void unlock();

	public:
		HANDLE m_mutex;
	};

	class critical_lock : public ILock
	{
	public:
		critical_lock();
		~critical_lock();

	public:
		void lock();
		void unlock();

	public:
		CRITICAL_SECTION m_cs;
	};

	class scope_lock
	{
	public:
		scope_lock(ILock* plock_entity);
		~scope_lock(void);

	public:
		void lock();
		void unlock();

	private:
		ILock* m_pLock;
	};
}
  • lock.c
#include "lock.h"
#include <tchar.h>

using namespace lock_summary;

mutex_lock::mutex_lock()
{
	m_mutex = ::CreateMutex(NULL, FALSE, __T("mutex"));
}

mutex_lock::~mutex_lock()
{
	::CloseHandle(m_mutex);
}

void mutex_lock::lock()
{
	unsigned int ret = ::WaitForSingleObject(m_mutex, INFINITE);
}

void mutex_lock::unlock()
{
	::ReleaseMutex(m_mutex);
}

critical_lock::critical_lock()
{
	::InitializeCriticalSection(&m_cs);
}

critical_lock::~critical_lock()
{
	::DeleteCriticalSection(&m_cs);
}

void critical_lock::lock()
{
	::EnterCriticalSection(&m_cs);
}
void critical_lock::unlock()
{
	::LeaveCriticalSection(&m_cs);
}

scope_lock::scope_lock(ILock* plock_entity)
{
	m_pLock = plock_entity;

	lock();

}


scope_lock::~scope_lock(void)
{
	unlock();
}

void scope_lock::lock()
{
	if (m_pLock != NULL)
	{
		m_pLock->lock();
	}
}


void scope_lock::unlock()
{
	if (m_pLock != NULL)
	{
		m_pLock->unlock();
	}
}
  • ThreadPool.h
#pragma once
#define   WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <list>
#include <vector>
#include "lock.h"

namespace base
{
	typedef int (*fun)(void* context);

	enum ThreadStatus
	{
		kerror = -1,
		knormal = 0,
		krun,
		kblock
	};

	class Thread
	{
	public:
		Thread(fun f, void* context);
		~Thread();

	public:
		void start();
		void stop();

		void resume()
		{
			::ResumeThread(_threadHandle);
			_bActive = true;
		}
		void suspend()
		{
			_bActive = false;
			::SuspendThread(_threadHandle);
		}
		bool active() { return _bActive; }
	  static unsigned int __stdcall threadProc(void* context);
		
	private:
		unsigned int _threadId;
		HANDLE _threadHandle;
		bool _bStop;
		fun _threadFun;
		void* _context;
		bool _bActive;
	};

	class Runable
	{
	public:
		virtual ~Runable(){};

		virtual void run() = 0;
	};

class ThreadPool
{
public:
	ThreadPool(void);
	~ThreadPool(void);

	void start(int threadNum);
	void stop();
	void runInThread(Runable* runobj);
	static int runLoop(void* context);
private: 
	std::list<Runable* > _taskQueue;
	std::vector<Thread> _threads;
	lock_summary::critical_lock _crclLock;
};
}
  • ThreadPool.cpp
#include "ThreadPool.h"
#include <process.h>
#include <assert.h>

using namespace base;

Thread::Thread(fun f, void* context):
_threadFun(f),_context(context),
_bActive(true),_bStop(false)
{
}

Thread::~Thread()
{
}

void Thread::start()
{
	_threadHandle = (HANDLE)_beginthreadex(NULL, 0, threadProc, this, 0, &_threadId);
}

void Thread::stop()
{
	_bStop = true;

	resume();
	int ret;
	ret = ::WaitForSingleObject(_threadHandle,100);
	if (WAIT_OBJECT_0 != ret)
	{
		DWORD dwExitCode;
		GetExitCodeThread(_threadHandle,&dwExitCode);
		if (STILL_ACTIVE ==dwExitCode)
		{
			//TRACE("STILL_ACTIVE ==dwExitCode\n");
			TerminateThread(_threadHandle,dwExitCode);
			::CloseHandle(_threadHandle);
		}
	}	
	_threadHandle = NULL;
}


unsigned int __stdcall Thread::threadProc(void* context)
{
	Thread* pthis = (Thread*)context;
	if (pthis == NULL)
	{
		return -1;
	}

	int runStatus = 0;
	while (!pthis->_bStop)
	{
		try
		{
			runStatus = pthis->_threadFun(pthis->_context);
			if (runStatus == kblock)
			{
				pthis->suspend();
			}
		}
		catch (std::exception& exc)
		{
		}
	}
	return 0;
}

ThreadPool::ThreadPool(void)
{
}

ThreadPool::~ThreadPool(void)
{
}

void ThreadPool::start(int threadNum)
{
	assert(threadNum > 0);

	_threads.reserve(threadNum);
	for (int i = 0; i < threadNum; i++)
	{
		_threads.push_back(Thread(&ThreadPool::runLoop, (void*)this));
		_threads[i].start();
	}
}

//1 stop all thread
//2 destroy all resource
void ThreadPool::stop()
{
	for (int i = 0; i < _threads.size(); i++)
	{
		_threads[i].stop();
	}

	{
		lock_summary::scope_lock lock(&_crclLock);
		std::list<Runable*>::iterator it;
		for (it = _taskQueue.begin(); it != _taskQueue.end(); )
		{
			delete (*it);
			*it = NULL;
			it = _taskQueue.erase(it);
		}
	}
}

void ThreadPool::runInThread(Runable* runobj)
{
	assert(runobj != NULL);
	{
		lock_summary::scope_lock lock(&_crclLock);
		_taskQueue.push_back(runobj);

		for (int i = 0; i < _threads.size(); i++)
		{
			if (!_threads[i].active())
			{
				_threads[i].resume();
				break;
			}
		}
	}
}

//1 many threads call this fun,lock
//2 if taskqueue is empty:suspend current thread
//
int ThreadPool::runLoop(void* context)
{
	ThreadPool* pthis = (ThreadPool*)context;
	if (pthis == NULL)
	{
		return kerror;
	}
	if (pthis->_taskQueue.empty())
		return kblock;
  //run
	Runable* runobj = NULL;
	{
		lock_summary::scope_lock lock(&pthis->_crclLock);
		runobj = pthis->_taskQueue.front();
		pthis->_taskQueue.pop_front();
	}
	runobj->run();
	delete runobj;
	runobj = NULL;
	
	return knormal;
}

参考资料:
《https://www.cnblogs.com/ailumiyana/p/10016965.html》
《https://zh.wikipedia.org/wiki/%E7%BA%BF%E7%A8%8B%E6%B1%A0》

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值