1.什么是線程池?
線程池(thread pool):是一種多線程的處理形式,處理過程種將任務添加到隊列,然后再創建線程后自動啟動這些任務。線程池在系統啟動時即創建大量空閑的線程,程序將一個任務傳給線程池,線程池就會啟動一條線程來執行這個任務,執行結束以后,該線程並不會死亡,而是再次返回線程池中成為空閑狀態,等待執行下一個任務。
2.為什么要使用線程池?
客戶端服務器通信過程中產生卡頓的原因:
• 1.線程的切換 *
• 2.內存的申請和釋放 **
• 3.線程的創建和銷毀 ***
多線程運行過程中,由於線程頻繁創建和銷毀帶來的效率的損失,會過度銷毀系統資源,以及過度切換線程的危險,從而導致可能系統資源崩潰。我們可使用線程池技術來避免。使用線程池技術可以更好地來提高性能。
3.線程池工作機制:
線程池編程模式下,任務是提交給整個線程池,而不是提交給某個線程,線程池拿到任務后就在內部尋找空閑的線程並將任務交給某個空閑的線程,一個線程只能執行一個任務,但可以同時向線程池提交多個任務。
4.如何設計一個線程池?
3.1 所有的線程都等待信號, 當有個請求來的時候, 就釋放一個信號, 拿到信號的線程干活, 干完活之后信號數減一。
3.2使用回調, 不同的請求封裝成不同的函數, 當對應請求來的時候, 將函數地址丟給線程運行。
3.3准備一個請求隊列, 當有請求來的時候, 將請求放到隊列,同時釋放一個信號. 拿到信號的線程會從隊列里面取出請求並處理. 處理完之后, 信號減一。
5.池技術在何時使用?
當資源申請和釋放比資源使用的效率帶來的損失大的時候使用
6.代碼示例:創建並使用一個簡單的線程池:
MyThreadPool.h:
#pragma once
#include<windows.h>
#include<queue>
using namespace std;
//任務接口
class IThreadPoolTask
{
public:
virtual ~IThreadPoolTask() {};
virtual void RunTask() = 0;
};
//線程池
class MyThreadPool
{
public:
MyThreadPool();
~MyThreadPool();
BOOL CreateThreadPool(DWORD dwThreadCount);
VOID DestroyThread();
void PostTask(IThreadPoolTask* pTask);
private:
static DWORD WINAPI TaskThread(LPVOID pParam);
private:
HANDLE m_hSemphore;//信號量,用於通知線程池中的線程,有新的請求來了
queue<IThreadPoolTask*> m_queueTasks;//任務隊列
DWORD m_dwTreadCount; //線程個數
CRITICAL_SECTION m_cs;//用於隊列的同步
};
MyThreadPool.cpp:
#include "MyThreadPool.h"
MyThreadPool::MyThreadPool()
{
InitializeCriticalSection(&m_cs);
}
MyThreadPool::~MyThreadPool()
{
DeleteCriticalSection(&m_cs);
}
BOOL MyThreadPool::CreateThreadPool(DWORD dwThreadCount)
{
m_hSemphore = CreateSemaphore(
NULL,
0,//沒有任務,則釋放出的信號為0
MAXLONG,//取最大任務個數
NULL
);
if (m_hSemphore == NULL)
{
return FALSE;
}
//創建線程
m_dwTreadCount = dwThreadCount;
for (DWORD i = 0; i < m_dwTreadCount; ++i)
{
HANDLE hThread = CreateThread(NULL, 0, TaskThread, this, 0, NULL);
CloseHandle(hThread);
}
}
void MyThreadPool::DestroyThread()
{
CloseHandle(m_hSemphore);
}
void MyThreadPool::PostTask(IThreadPoolTask* pTask)
{
EnterCriticalSection(&m_cs);
//向隊列種添加任務同時添加一下信號
m_queueTasks.push(pTask);
ReleaseSemaphore(m_hSemphore, 1, NULL);
//
if (m_queueTasks.size() > 2)
{
HANDLE hThread = CreateThread(NULL, 0, TaskThread, this, 0, NULL);
CloseHandle(hThread);
}
LeaveCriticalSection(&m_cs);
}
DWORD WINAPI MyThreadPool::TaskThread(LPVOID pParam)
{
MyThreadPool* pThis = (MyThreadPool*)pParam;
//從隊列種拿出任務,執行任務
while (TRUE)
{
DWORD dwRet = WaitForSingleObject(pThis->m_hSemphore, INFINITE);
//線程池被銷毀
if (dwRet == WAIT_FAILED)
{
return 0;
}
//從隊列種取出任務
EnterCriticalSection(&pThis->m_cs);
IThreadPoolTask* pTask = pThis->m_queueTasks.front();
pThis->m_queueTasks.pop();
LeaveCriticalSection(&pThis->m_cs);
//執行任務
pTask->RunTask();
delete pTask;
}
return 0;
}
測試:使用線程池創建線程:
#include <iostream> #include"MyThreadPool.h" class CMyTask :public IThreadPoolTask { public: virtual void RunTask() override { printf("tid: %d \tidx:%d \r\n", GetCurrentThreadId(), m_nIdx); } CMyTask(int nIdx): m_nIdx(nIdx) {} ~CMyTask() {} private: int m_nIdx = 0; }; int main() { MyThreadPool tp; tp.CreateThreadPool(4); for (int i = 0; i < 100; ++i) { CMyTask* pTask = new CMyTask(i); tp.PostTask(pTask); } system("pause"); return 0; }
效果: