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》