下來我們看一下如何使用Delphi編寫一個IOCP的線程池。
創建一個IOCP線程池,至少需要2個基本類。
首先:我們定義一個管理工作線程的線程池類(TTheadPool),這個類用來將接收到的數據轉發給相應的工作線程類,同時將工作線程類返回的數據進行排隊處理。
其次:我們定義真正處理數據的工作線程類,此類用來對線程池類發來的數據進行相應的處理,同時將處理返回的數據通過線程池類返回。
首先:我們定義一個管理工作線程的線程池類(TTheadPool),這個類用來將接收到的數據轉發給相應的工作線程類,同時將工作線程類返回的數據進行排隊處理。
其次:我們定義真正處理數據的工作線程類,此類用來對線程池類發來的數據進行相應的處理,同時將處理返回的數據通過線程池類返回。
現在我們分別來看一下這兩個類如何定義和編寫。
TTheadPool的作用是用來管理工作線程類,那麼它至少需要 創建工作線程函數和釋放工作線程函數。
//IOCP管理
m_hCompPort: THandle;
m_iWorkThreads: Word;
m_pWorkThreads: array of TWorkThread;
function CreateWorkThreads: Boolean;
function DestroyWorkThreads: Boolean;
procedure CreateCompPort;
procedure CloseCompPort;
TTheadPool的作用是用來管理工作線程類,那麼它至少需要 創建工作線程函數和釋放工作線程函數。
//IOCP管理
m_hCompPort: THandle;
m_iWorkThreads: Word;
m_pWorkThreads: array of TWorkThread;
function CreateWorkThreads: Boolean;
function DestroyWorkThreads: Boolean;
procedure CreateCompPort;
procedure CloseCompPort;
m_hCompPort是完成端口
m_iWorkThreads是用來記錄創建工作線程的數量
m_pWorkThreads是工作線程的動態數組
CreateWorkThreads函數用來創建工作線程
DestroyWorkThreads函數用來銷燬工作線程
CreateCompPort函數用來創建完成端口
CloseCompPort函數用來關閉完成端口
下來看一下以上函數的實現
function TTheadPool.CreateWorkThreads: Boolean;
var
I: Integer;
begin
if m_pWorkThreads <> nil then
begin
Result:=True;
Exit;
end;
function TTheadPool.CreateWorkThreads: Boolean;
var
I: Integer;
begin
if m_pWorkThreads <> nil then
begin
Result:=True;
Exit;
end;
m_iWorkThreads:=0;
if m_WorkCount = 0 then
SetLength(m_pWorkThreads, getNumberOfProcessor * 2 + 2)
else
SetLength(m_pWorkThreads, m_WorkCount);
SetLength(m_pWorkThreads, getNumberOfProcessor * 2 + 2)
else
SetLength(m_pWorkThreads, m_WorkCount);
for I:=Low(m_pWorkThreads) to High(m_pWorkThreads) do
begin
m_pWorkThreads[i]:=TWorkThread.Create(Self);
Inc(m_iWorkThreads);
end;
begin
m_pWorkThreads[i]:=TWorkThread.Create(Self);
Inc(m_iWorkThreads);
end;
if m_iWorkThreads = 0 then
begin
Result:=False;
Exit;
end;
Result:=True;
end;
begin
Result:=False;
Exit;
end;
Result:=True;
end;
function TTheadPool.DestroyWorkThreads: Boolean;
var
I: Integer;
begin
if m_pWorkThreads = nil then
begin
Result:=True;
Exit;
end;
var
I: Integer;
begin
if m_pWorkThreads = nil then
begin
Result:=True;
Exit;
end;
for i:=0 to m_iWorkThreads - 1 do
begin
PostQueuedCompletionStatus(m_hCompPort,0, 0, Pointer(SHUTDOWN_FLAG));
end;
begin
PostQueuedCompletionStatus(m_hCompPort,0, 0, Pointer(SHUTDOWN_FLAG));
end;
Sleep(0);
for I:=Low(m_pWorkThreads) to High(m_pWorkThreads) do
begin
m_pWorkThreads[i].Destroy;
end;
begin
m_pWorkThreads[i].Destroy;
end;
Result:=True;
end;
end;
procedure TTheadPool.CreateCompPort;
begin
if m_hCompPort = 0 then
begin
m_hCompPort:=CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
end;
end;
begin
if m_hCompPort = 0 then
begin
m_hCompPort:=CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
end;
end;
procedure TTheadPool.CloseCompPort;
begin
if m_hCompPort <> 0 then
begin
CloseHandle(m_hCompPort);
m_hCompPort:=0;
end;
end;
begin
if m_hCompPort <> 0 then
begin
CloseHandle(m_hCompPort);
m_hCompPort:=0;
end;
end;
//數據合併與管理
QueueCS: TRTLCriticalSection;
First, Last: PMsgBuffer;
procedure Reponse;
procedure FreeQueue;
這段代碼是用來加載消息隊列,
Reponse函數對消息隊列的消息進行處理
FreeQueue函數是用來當程序結束時,銷燬未處理的隊列消息
procedure TTheadPool.Reponse;
var
pWork,
pNext: PMsgBuffer;
begin
pWork:=nil;
pNext:=nil;
EnterCriticalSection(QueueCS);
try
if not Assigned(pWork) then
begin
pWork:=First;
First:=nil;
Last:=nil;
end;
finally
LeaveCriticalSection(QueueCS);
end;
var
pWork,
pNext: PMsgBuffer;
begin
pWork:=nil;
pNext:=nil;
EnterCriticalSection(QueueCS);
try
if not Assigned(pWork) then
begin
pWork:=First;
First:=nil;
Last:=nil;
end;
finally
LeaveCriticalSection(QueueCS);
end;
while Assigned(pWork) do
begin
pNext:=pWork.Next;
PostQueuedCompletionStatus(m_hCompPort, 0, 0, Pointer(pWork));
pWork:=pNext;
end;
end;
begin
pNext:=pWork.Next;
PostQueuedCompletionStatus(m_hCompPort, 0, 0, Pointer(pWork));
pWork:=pNext;
end;
end;
procedure TTheadPool.FreeQueue;
var
pNext: PMsgBuffer;
begin
while Assigned(First) do
begin
pNext:=First.Next;
if Assigned(First.Buffer) then
begin
FreeMem(First.Buffer);
end;
Dispose(First);
First:=pNext;
end;
First:=nil;
Last:=nil;
end;
var
pNext: PMsgBuffer;
begin
while Assigned(First) do
begin
pNext:=First.Next;
if Assigned(First.Buffer) then
begin
FreeMem(First.Buffer);
end;
Dispose(First);
First:=pNext;
end;
First:=nil;
Last:=nil;
end;
以上就是線程池管理類(TTheadPool)的處理過程和主要函數。
對於工作線程類來說處理起來就比較簡單了。
TWorkThread = class(TThread)
protected
procedure Execute; override;
public
procedure Reponse(pMsg: PMsgBuffer);
procedure TestWork(pBuf: Pchar; iBufLen, iSocketHandle: Integer);
public
Parent: TTheadPool;
Constructor Create(TheadPool: TTheadPool);
Destructor Destroy; Override;
end;
Reponse函數是用來對於線程池管理類發送過來的消息進行分發的函數。
TestWork函數是對於線程池管理類發送過來的數據進行處理的實際的函數。
TWorkThread = class(TThread)
protected
procedure Execute; override;
public
procedure Reponse(pMsg: PMsgBuffer);
procedure TestWork(pBuf: Pchar; iBufLen, iSocketHandle: Integer);
public
Parent: TTheadPool;
Constructor Create(TheadPool: TTheadPool);
Destructor Destroy; Override;
end;
Reponse函數是用來對於線程池管理類發送過來的消息進行分發的函數。
TestWork函數是對於線程池管理類發送過來的數據進行處理的實際的函數。
procedure TWorkThread.Execute;
var
bRet: BOOL;
iRecvlen: Cardinal;
pMsg: PCRCMsgBuffer;
iBufferlen: Cardinal;
begin
while not Terminated do
begin
try
pMsg:=nil;
bRet:=GetQueuedCompletionStatus(Parent.m_hCompPort, iBufferlen, iRecvlen, POverlapped(pMsg), INFINITE);
var
bRet: BOOL;
iRecvlen: Cardinal;
pMsg: PCRCMsgBuffer;
iBufferlen: Cardinal;
begin
while not Terminated do
begin
try
pMsg:=nil;
bRet:=GetQueuedCompletionStatus(Parent.m_hCompPort, iBufferlen, iRecvlen, POverlapped(pMsg), INFINITE);
//主動斷開
if (Cardinal(pMsg) = SHUTDOWN_FLAG) then
begin
Terminate;
end;
if (Cardinal(pMsg) = SHUTDOWN_FLAG) then
begin
Terminate;
end;
//關閉
if Terminated then
begin
Break;
end;
if Terminated then
begin
Break;
end;
if bRet and Assigned(pMsg) then
begin
Reponse(pMsg);
end;
except
Continue;
end;
end;
end;
begin
Reponse(pMsg);
end;
except
Continue;
end;
end;
end;
procedure TCRCWorkThread.Reponse(pMsg: PCRCMsgBuffer);
begin
case pMsg.MainType of
USER_TEST: //用戶發送來的相關協議
TestWork(pMsg.pData, pMsg.iDatalen, pMsg.SocketHandle);
else
g_MainThread.AddLog(Format('未知的信息:%d',[pMsg.MainType]));
end;
begin
case pMsg.MainType of
USER_TEST: //用戶發送來的相關協議
TestWork(pMsg.pData, pMsg.iDatalen, pMsg.SocketHandle);
else
g_MainThread.AddLog(Format('未知的信息:%d',[pMsg.MainType]));
end;
if Assigned(pMsg.pData) then
begin
FreeMem(pMsg.pData);
end;
Dispose(pMsg);
end;
begin
FreeMem(pMsg.pData);
end;
Dispose(pMsg);
end;
這樣我們就將一個IOCP的線程池編寫完成,其中需要注意的2點。
1:SHUTDOWN_FLAG是自己定義的一個常量,當線程池類需要關閉的時候,線程池類可以通過PostQueuedCompletionStatus向每一個工作線程發送消息,通知工作線程關閉
2:在線程池類中申請的內存PMsgBuffer 是在工作線程中進行釋放的。
1:SHUTDOWN_FLAG是自己定義的一個常量,當線程池類需要關閉的時候,線程池類可以通過PostQueuedCompletionStatus向每一個工作線程發送消息,通知工作線程關閉
2:在線程池類中申請的內存PMsgBuffer 是在工作線程中進行釋放的。
以上就是IOCP線程池的相關內容,希望通過我的BLOG,可以讓大家能夠熟知IOCP線程池其實沒有那麼的神祕。