线程池
前缀知识
std::shared_ptr
std::shared_ptr是在c++11中引入的一种智能指针,其特点是它所指向的资源具有共享性,即多个shared_ptr可以指向同一份资源。在c++中使用shared_ptr需要包含<memory>头文件。
创建方式
shared_ptr<Investment> sp(newInvestment()); //new shared_ptr<Investment> sp1 = std::make_shared<Investment>(); //make_shared shared_ptr<Investment> sp2 = sp1; //拷贝
std::shared_ptr 是一种智能指针,它能够记录多少个 shared_ptr 共同指向一个对象,从而消除显示的调用 delete,当引用计数变为零的时候就会将对象自动删除。 std::shared_ptr 可以通过 get() 方法来获取原始指针,通过 reset() 来减少一个引用计数, 并通过use_count()来查看一个对象的引用计数。
shared_ptr的循环引用会造成内存泄漏
struct Node { int _val; std::shared_ptr<Node> _prev; std::shared_ptr<Node> _next; ~Node() { std::cout << "~Node()" << std::endl; } }; int main() { std::shared_ptr<Node> n1(new Node); std::shared_ptr<Node> n2(new Node); n1->_next = n2; n2->_prev = n1; return 0; }
std::thread
每一个 C++11 程序都包含一个主线程即 main() 函数,在 C++11 中可以通过创建 std::thread 对象来创建新的线程,每个 std::thread 对象都可以与一个线程相关联。
需要引用的头文件:
#include <thread>
调用 thread 将立即同时开始执行这个新建立的线程,之后 main() 的主线程也会继续执行
(1)get_id(): 取得目前的线程 id,回传一个为 std::thread::id 的类型 (2)joinable(): 检查是否可join (3)join(): 等待线程完成 (4)detach(): 与该线程分离,一旦该线程执行完后它所分配的资源会被释放 (5)native_handle(): 取得平台原生的native handle
#include <iostream> #include <thread> void foo() { std::cout << "foo\n"; } void bar(int x) { std::cout << "bar\n"; } int main() { std::thread t1(foo); // 建立一个新线程且执行 foo 函数 std::thread t2(bar, 0); // 建立一個新线程且执行 bar 函数 std::cout << "main, foo and bar now execute concurrently...\n"; // synchronize threads std::cout << "sleep 1s\n"; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "join t1\n"; t1.join(); // 等待 t1 线程结束 std::cout << "join t2\n"; t2.join(); // 等待 t2 线程结束 std::cout << "foo and bar completed.\n"; return 0; }
在 main 主线程建立 t1 线程后,主线程便继续往下执行,如果主线程需要等 t1 执行完毕后才能继续执行的话就需要使用 join,即等待 t1 线程执行完 foo 后主先线程才能继续执行,否则主线程会一直阻塞在 join 这一行
承上例,如果主线程不想等或是可以不用等待 t1 线程的话。就可以使用 detach 来让 t1 线程分离,接着主线程就可以继续执行,t1线程也在继续执行,在整个程序结束前最好养成好习惯确保所有子线程都已执行完毕,因为在 linux 系统如果主线程执行结束还有子线程在执行的话会报错。
CRITICAL_SECTION 临界区
CRITICAL_SECTION不是针对于资源的,而是针对于不同线程间的代码段的,我们能够用它来进行所谓资源的“锁定”,其实是因为我们在任何访问共享资源的地方都加入了EnterCriticalSection和 LeaveCriticalSection语句,使得同一时间只能够有一个线程的代码段访问到该共享资源而已(其它想访问该资源的代码段不得不等待)。
初始化临界区
::InitializeCriticalSection
::CreateSemaphore 创建原子数
线程
bind需要头文件 #include <functional>
把一个成员函数变成全局静态函数
头文件:
#pragma once #include <thread> #include <memory> class CThread { public: CThread(); virtual ~CThread(); CThread(const CThread& rhs) = delete; CThread& operator=(const CThread& rhs) = delete; void Start(); virtual void Stop() = 0; void Join(); protected: virtual void Run() = 0; private: void ThreadProc(); protected: bool m_bStop{ false }; private: std::shared_ptr<std::thread> m_spThread; };
源文件
#include "stdafx.h" #include "Thread.h" #include <functional>//bind CThread::CThread() { } CThread::~CThread() { } void CThread::Start() { m_spThread.reset(new std::thread(std::bind(&CThread::ThreadProc, this))); } void CThread::ThreadProc() { Run(); } void CThread::Join() { m_spThread->join(); }
线程池
线程池的工作原理是这样的:
线程池主要有线程信息数组、任务信息数组。线程信息临界区和任务信息临界区。
线程池初始化:
初始化的时候有线程数量,首先创造临界区。然后进入线程信息临界区,根据线程数量,创造线程
hThread = (HANDLE)(ULONG_PTR)_beginthreadex(NULL, 0, &ThreadProc, lpThreadInfo, CREATE_SUSPENDED, (UINT*)&dwThreadID);
注意,创造线程的时候,这里线程是挂起的CREATE_SUSPENDED。也就是说线程还没开始运行
运行的线程函数是ThreadProc,传递的参数是线程信息 TP_THREAD_INFO
线程信息填的是创建线程的Handle和线程池指针。
创建好线程后,就把线程放入到线程信息数组里。
线程池添加任务:
就是进入任务信息临界区,然后把任务放入到任务信息临界区
执行的线程函数:
ThreadProc,执行的线程函数就是会有一个无限循环。
在这个无限循环里,会从任务信息数组里取出任务,然后去执行这个任务。
先是进入任务信息数组临界区,然后遍历这个数组,看看哪个任务还没有被执行,然后取出这个任务,把这个任务设为已执行。然后退出遍历。开始执行这个任务。
执行完任务之后,就再进入到任务信息临界区,然后遍历数组,找到这个任务在数组中的位置,然后删除掉这个任务。
之后又进入到大循环里。
头文件
#pragma once #include <vector> class CThreadPoolTask { public: CThreadPoolTask(); virtual ~CThreadPoolTask(); public: virtual BOOL IsRunning(); virtual void SetRunning(BOOL bRunning); virtual int Run(); virtual int Stop(); //让正在运行的任务结束的接口(必须实现) virtual void TaskFinish(); //重载这个函数让已经结束的任务提供一个清理资源的机会 private: BOOL m_bRunning; }; struct TP_THREAD_INFO // 线程信息结构 { HANDLE hThread; LPARAM lParam; }; class CThreadPool { public: CThreadPool(); ~CThreadPool(); public: BOOL Init(int nThreadNums); void UnInit(); BOOL AddTask(CThreadPoolTask* lpTask); BOOL InsertTask(int nIndex, CThreadPoolTask* lpTask); void RemoveTask(CThreadPoolTask* lpTask); void RemoveAllTask(); private: static UINT APIENTRY ThreadProc(LPVOID lpParam); // 线程函数 private: std::vector<TP_THREAD_INFO*> m_arrThreadInfo; // 线程信息数组 std::vector<CThreadPoolTask*> m_arrTask; // 任务信息数组 CRITICAL_SECTION m_csThreadInfo; // 线程信息临界区 CRITICAL_SECTION m_csTask; // 任务信息临界区 HANDLE m_hSemaphore_Task; // 任务通知信号量 HANDLE m_hEvent_Exit; // 退出线程通知事件句柄 HANDLE m_hEvent_ExitAll; // 所有线程均退出完成通知事件句柄 long m_lThreadNums; // 线程总数 long m_lRunningThreadNums; // 正在工作中的线程计数 };
源文件
#include "stdafx.h" #include "ThreadPool.h" CThreadPoolTask::CThreadPoolTask() { m_bRunning = FALSE; } CThreadPoolTask::~CThreadPoolTask() { } BOOL CThreadPoolTask::IsRunning() { return m_bRunning; } void CThreadPoolTask::SetRunning(BOOL bRunning) { m_bRunning = bRunning; if (!m_bRunning) TaskFinish(); } int CThreadPoolTask::Run() { return 0; } int CThreadPoolTask::Stop() { return 0; } void CThreadPoolTask::TaskFinish() { return; } CThreadPool::CThreadPool() { memset(&m_csThreadInfo, 0, sizeof(CRITICAL_SECTION)); memset(&m_csTask, 0, sizeof(CRITICAL_SECTION)); m_hSemaphore_Task = NULL; m_hEvent_Exit = NULL; m_hEvent_ExitAll = NULL; m_lThreadNums = 0; m_lRunningThreadNums = 0; } CThreadPool::~CThreadPool() { } BOOL CThreadPool::Init(int nThreadNums) { TP_THREAD_INFO* lpThreadInfo; HANDLE hThread; DWORD dwThreadID; if (nThreadNums <= 0) return FALSE; m_lThreadNums = 0; m_lRunningThreadNums = 0; ::InitializeCriticalSection(&m_csThreadInfo); ::InitializeCriticalSection(&m_csTask); m_hSemaphore_Task = ::CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); m_hEvent_Exit = ::CreateEvent(NULL, TRUE, FALSE, NULL); m_hEvent_ExitAll = ::CreateEvent(NULL, FALSE, FALSE, NULL); ::EnterCriticalSection(&m_csThreadInfo); for (int i = 0; i < nThreadNums; i++) { lpThreadInfo = new TP_THREAD_INFO; if (lpThreadInfo != NULL) { hThread = (HANDLE)(ULONG_PTR)_beginthreadex(NULL, 0, &ThreadProc, lpThreadInfo, CREATE_SUSPENDED, (UINT*)&dwThreadID); //开启一个线程,CREATE_SUSPENDED让线程等一会儿再运行 if (hThread != NULL) { lpThreadInfo->hThread = hThread; lpThreadInfo->lParam = (LPARAM)this; m_arrThreadInfo.push_back(lpThreadInfo); ::ResumeThread(hThread); //恢复一个被暂停的线程 } else { delete lpThreadInfo; } } } ::LeaveCriticalSection(&m_csThreadInfo); return TRUE; } void CThreadPool::UnInit() { RemoveAllTask(); ::SetEvent(m_hEvent_Exit); ::WaitForSingleObject(m_hEvent_ExitAll, 30*1000); // 等待所有线程退出完毕,超时时间设为30秒 ::EnterCriticalSection(&m_csThreadInfo); for (int i = 0; i < (int)m_arrThreadInfo.size(); i++) { TP_THREAD_INFO* lpThreadInfo = m_arrThreadInfo[i]; if (lpThreadInfo != NULL) { ::CloseHandle(lpThreadInfo->hThread); DWORD dwExitCode = 0; ::GetExitCodeThread(lpThreadInfo->hThread, &dwExitCode); if (STILL_ACTIVE == dwExitCode) ::TerminateThread(lpThreadInfo->hThread, 0); delete lpThreadInfo; } } m_arrThreadInfo.clear(); ::LeaveCriticalSection(&m_csThreadInfo); ::EnterCriticalSection(&m_csTask); m_arrTask.clear(); ::LeaveCriticalSection(&m_csTask); if (m_hSemaphore_Task != NULL) { ::CloseHandle(m_hSemaphore_Task); m_hSemaphore_Task = NULL; } if (m_hEvent_Exit != NULL) { ::CloseHandle(m_hEvent_Exit); m_hEvent_Exit = NULL; } if (m_hEvent_ExitAll != NULL) { ::CloseHandle(m_hEvent_ExitAll); m_hEvent_ExitAll = NULL; } ::DeleteCriticalSection(&m_csThreadInfo); ::DeleteCriticalSection(&m_csTask); } BOOL CThreadPool::AddTask(CThreadPoolTask* lpTask) { if (NULL == lpTask) return FALSE; ::EnterCriticalSection(&m_csTask); m_arrTask.push_back(lpTask); ::LeaveCriticalSection(&m_csTask); ::ReleaseSemaphore(m_hSemaphore_Task, 1, NULL); // 增加任务通知信号量 return TRUE; } BOOL CThreadPool::InsertTask(int nIndex, CThreadPoolTask* lpTask) { if (NULL == lpTask) return FALSE; ::EnterCriticalSection(&m_csTask); m_arrTask.insert(m_arrTask.begin() + nIndex, lpTask); ::LeaveCriticalSection(&m_csTask); ::ReleaseSemaphore(m_hSemaphore_Task, 1, NULL); // 增加任务通知信号量 return TRUE; } void CThreadPool::RemoveTask(CThreadPoolTask* lpTask) { if (NULL == lpTask) return; ::EnterCriticalSection(&m_csTask); for (int i = 0; i < (int)m_arrTask.size(); i++) { if (m_arrTask[i] == lpTask) { if (lpTask->IsRunning()) { lpTask->Stop(); } else { m_arrTask.erase(m_arrTask.begin() + i); lpTask->TaskFinish(); } break; } } ::LeaveCriticalSection(&m_csTask); } void CThreadPool::RemoveAllTask() { ::EnterCriticalSection(&m_csTask); for (int i = (int)m_arrTask.size() - 1; i >= 0; i--) { CThreadPoolTask* lpTask = m_arrTask[i]; if (lpTask != NULL) { if (lpTask->IsRunning()) { lpTask->Stop(); } else { m_arrTask.erase(m_arrTask.begin() + i); lpTask->TaskFinish(); } } } ::LeaveCriticalSection(&m_csTask); } // 线程函数 UINT CThreadPool::ThreadProc(LPVOID lpParam) { TP_THREAD_INFO* lpThreadInfo; CThreadPool* lpThis; CThreadPoolTask* lpTask,* lpTempTask; HANDLE hWaitEvent[2]; DWORD dwIndex; lpThreadInfo = (TP_THREAD_INFO*)lpParam; if (NULL == lpThreadInfo) return 0; lpThis = (CThreadPool*)lpThreadInfo->lParam; if (NULL == lpThis) return 0; ::InterlockedIncrement(&lpThis->m_lThreadNums); // 增加线程计数 hWaitEvent[0] = lpThis->m_hEvent_Exit; hWaitEvent[1] = lpThis->m_hSemaphore_Task; for (;;) { dwIndex = ::WaitForMultipleObjects(2, hWaitEvent, FALSE, INFINITE); //填true就是所有事件发出信号,那么就响应。填false就是任何事件发出信号,那么就响应。 if (dwIndex == WAIT_OBJECT_0) // 接到退出线程通知事件 break; lpTask = NULL; ::EnterCriticalSection(&lpThis->m_csTask); // 取任务 for (int i = 0; i < (int)lpThis->m_arrTask.size(); i++) { lpTempTask = lpThis->m_arrTask[i]; if (lpTempTask != NULL) { if (!lpTempTask->IsRunning()) { lpTempTask->SetRunning(TRUE); lpTask = lpTempTask; break; } } } ::LeaveCriticalSection(&lpThis->m_csTask); if (lpTask != NULL) // 有任务 { ::InterlockedIncrement(&lpThis->m_lRunningThreadNums); // 增加正在工作中线程计数 //有任务取任务,并执行任务,执行完就把任务删除了 lpTask->Run(); ::EnterCriticalSection(&lpThis->m_csTask); // 删除任务 for (int i = 0; i < (int)lpThis->m_arrTask.size(); i++) { lpTempTask = lpThis->m_arrTask[i]; if (lpTempTask == lpTask) { lpThis->m_arrTask.erase(lpThis->m_arrTask.begin() + i); break; } } lpTask->SetRunning(FALSE); ::LeaveCriticalSection(&lpThis->m_csTask); ::InterlockedDecrement(&lpThis->m_lRunningThreadNums); // 减少正在工作中线程计数 } } ::InterlockedDecrement(&lpThis->m_lThreadNums); // 减少线程计数 if (lpThis->m_lThreadNums <= 0) // 所有线程都结束完毕,置事件句柄有信号 ::SetEvent(lpThis->m_hEvent_ExitAll); return 0; }