这里有一个article from Delphi3000,展示了如何使用 IOCP 创建线程池。我不是此代码的作者,但作者的信息在源代码中。
我在这里重新发布 cmets 和代码:
现在每个人都应该明白什么
线程是,线程的原理
等等。对于有需要的人,
线程的简单功能是
从一个线程单独处理到
另一个,允许并发和
并行执行。主要原理
线程数也一样简单,内存
分配的在
必须对线程进行编组以确保
访问的安全性。有数
其他原则,但这确实是
关心的人。
然后……
线程安全队列将允许
多个线程添加和删除,
推送和弹出值
在 First on First off 上安全排队
基础。以高效和良好的
书面队列你可以有一个高度
开发中的有用组件
线程应用程序,来自帮助
使用线程安全日志记录,到
请求的异步处理。
线程池只是一个线程或一个
最多的线程数
通常用于管理一个队列
要求。例如网络服务器
这将有一个连续的队列
需要处理的请求使用
用于管理 http 的线程池
请求,或 COM+ 或 DCOM 服务器
使用线程池来处理 rpc
要求。这是这样做的,所以有
从一个处理的影响较小
请求另一个,说如果你跑了 3
同步请求和第一个
请求需要 1 分钟才能完成,
后两个请求将无法完成
在顶部添加至少 1 分钟
有自己的时间来处理,并为
大多数客户这不是
可以接受。
那么如何做到这一点..
从队列开始!!
Delphi 确实提供了一个 TQueue 对象
这是可用的,但
不幸的是不是线程安全的,也不是
真的太有效率了,但是人
应该查看 Contnrs.pas 文件以
看看 borland 如何在那里写堆栈和
排队。主要只有两个
队列所需的功能,这些
是添加和删除/推送和弹出。
Add/push 将添加一个值、指针或
对象到队列的末尾。和
remove/pop 将删除并返回
队列中的第一个值。
你可以从 TQueue 对象派生
并覆盖受保护的方法和
添加关键部分,这将
给你一些帮助,但我会
希望我的队列等到新的
请求在队列中,并将
线程进入休息状态,而它
等待新的请求。这可能是
通过添加互斥锁或信号来完成
事件,但有更简单的方法。这
windows api 提供 IO 完成
为我们提供线程的队列
安全访问队列,以及
在等待新请求的同时休息
队列。
实现线程池
线程池将非常
简单,将管理 x 个
所需的线程并通过每个队列
请求提供的事件
处理。很少需要
实现一个 TThread 类和你的
要实现的逻辑和
封装在执行事件中
类的,因此一个简单的
可以创建 TSimpleThread 类
它将执行任何方法中的任何
在另一个上下文中的对象
线。人们一旦明白这一点,
所有你需要关心的
已分配内存。
这是它的实现方式。
TThreadQueue 和 TThreadPool
实施
(* Implemented for Delphi3000.com Articles, 11/01/2004
Chris Baldwin
Director & Chief Architect
Alive Technology Limited
http://www.alivetechnology.com
*)
unit ThreadUtilities;
uses Windows, SysUtils, Classes;
type
EThreadStackFinalized = class(Exception);
TSimpleThread = class;
// Thread Safe Pointer Queue
TThreadQueue = class
private
FFinalized: Boolean;
FIOQueue: THandle;
public
constructor Create;
destructor Destroy; override;
procedure Finalize;
procedure Push(Data: Pointer);
function Pop(var Data: Pointer): Boolean;
property Finalized: Boolean read FFinalized;
end;
TThreadExecuteEvent = procedure (Thread: TThread) of object;
TSimpleThread = class(TThread)
private
FExecuteEvent: TThreadExecuteEvent;
protected
procedure Execute(); override;
public
constructor Create(CreateSuspended: Boolean; ExecuteEvent: TThreadExecuteEvent; AFreeOnTerminate: Boolean);
end;
TThreadPoolEvent = procedure (Data: Pointer; AThread: TThread) of Object;
TThreadPool = class(TObject)
private
FThreads: TList;
FThreadQueue: TThreadQueue;
FHandlePoolEvent: TThreadPoolEvent;
procedure DoHandleThreadExecute(Thread: TThread);
public
constructor Create( HandlePoolEvent: TThreadPoolEvent; MaxThreads: Integer = 1); virtual;
destructor Destroy; override;
procedure Add(const Data: Pointer);
end;
implementation
{ TThreadQueue }
constructor TThreadQueue.Create;
begin
//-- Create IO Completion Queue
FIOQueue := CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
FFinalized := False;
end;
destructor TThreadQueue.Destroy;
begin
//-- Destroy Completion Queue
if (FIOQueue <> 0) then
CloseHandle(FIOQueue);
inherited;
end;
procedure TThreadQueue.Finalize;
begin
//-- Post a finialize pointer on to the queue
PostQueuedCompletionStatus(FIOQueue, 0, 0, Pointer($FFFFFFFF));
FFinalized := True;
end;
(* Pop will return false if the queue is completed *)
function TThreadQueue.Pop(var Data: Pointer): Boolean;
var
A: Cardinal;
OL: POverLapped;
begin
Result := True;
if (not FFinalized) then
//-- Remove/Pop the first pointer from the queue or wait
GetQueuedCompletionStatus(FIOQueue, A, Cardinal(Data), OL, INFINITE);
//-- Check if we have finalized the queue for completion
if FFinalized or (OL = Pointer($FFFFFFFF)) then begin
Data := nil;
Result := False;
Finalize;
end;
end;
procedure TThreadQueue.Push(Data: Pointer);
begin
if FFinalized then
Raise EThreadStackFinalized.Create('Stack is finalized');
//-- Add/Push a pointer on to the end of the queue
PostQueuedCompletionStatus(FIOQueue, 0, Cardinal(Data), nil);
end;
{ TSimpleThread }
constructor TSimpleThread.Create(CreateSuspended: Boolean;
ExecuteEvent: TThreadExecuteEvent; AFreeOnTerminate: Boolean);
begin
FreeOnTerminate := AFreeOnTerminate;
FExecuteEvent := ExecuteEvent;
inherited Create(CreateSuspended);
end;
procedure TSimpleThread.Execute;
begin
if Assigned(FExecuteEvent) then
FExecuteEvent(Self);
end;
{ TThreadPool }
procedure TThreadPool.Add(const Data: Pointer);
begin
FThreadQueue.Push(Data);
end;
constructor TThreadPool.Create(HandlePoolEvent: TThreadPoolEvent;
MaxThreads: Integer);
begin
FHandlePoolEvent := HandlePoolEvent;
FThreadQueue := TThreadQueue.Create;
FThreads := TList.Create;
while FThreads.Count < MaxThreads do
FThreads.Add(TSimpleThread.Create(False, DoHandleThreadExecute, False));
end;
destructor TThreadPool.Destroy;
var
t: Integer;
begin
FThreadQueue.Finalize;
for t := 0 to FThreads.Count-1 do
TThread(FThreads[t]).Terminate;
while (FThreads.Count > 0) do begin
TThread(FThreads[0]).WaitFor;
TThread(FThreads[0]).Free;
FThreads.Delete(0);
end;
FThreadQueue.Free;
FThreads.Free;
inherited;
end;
procedure TThreadPool.DoHandleThreadExecute(Thread: TThread);
var
Data: Pointer;
begin
while FThreadQueue.Pop(Data) and (not TSimpleThread(Thread).Terminated) do begin
try
FHandlePoolEvent(Data, Thread);
except
end;
end;
end;
end.
如您所见,它非常直
前进,这样你就可以
很容易实现任何排队
通过线程请求,实际上是任何
要求的类型
线程可以使用这些来完成
反对并为您节省大量时间
努力。
您可以使用它来排队请求
从一个线程到多个线程,
或排队来自多个
线程到一个线程,这使得
这是一个很好的解决方案。
这里有一些使用这些的例子
对象。
线程安全日志记录
允许多个
异步写入的线程
日志文件。
uses Windows, ThreadUtilities,...;
type
PLogRequest = ^TLogRequest;
TLogRequest = record
LogText: String;
end;
TThreadFileLog = class(TObject)
private
FFileName: String;
FThreadPool: TThreadPool;
procedure HandleLogRequest(Data: Pointer; AThread: TThread);
public
constructor Create(const FileName: string);
destructor Destroy; override;
procedure Log(const LogText: string);
end;
implementation
(* Simple reuse of a logtofile function for example *)
procedure LogToFile(const FileName, LogString: String);
var
F: TextFile;
begin
AssignFile(F, FileName);
if not FileExists(FileName) then
Rewrite(F)
else
Append(F);
try
Writeln(F, DateTimeToStr(Now) + ': ' + LogString);
finally
CloseFile(F);
end;
end;
constructor TThreadFileLog.Create(const FileName: string);
begin
FFileName := FileName;
//-- Pool of one thread to handle queue of logs
FThreadPool := TThreadPool.Create(HandleLogRequest, 1);
end;
destructor TThreadFileLog.Destroy;
begin
FThreadPool.Free;
inherited;
end;
procedure TThreadFileLog.HandleLogRequest(Data: Pointer; AThread: TThread);
var
Request: PLogRequest;
begin
Request := Data;
try
LogToFile(FFileName, Request^.LogText);
finally
Dispose(Request);
end;
end;
procedure TThreadFileLog.Log(const LogText: string);
var
Request: PLogRequest;
begin
New(Request);
Request^.LogText := LogText;
FThreadPool.Add(Request);
end;
因为这是记录到一个文件,它会
将所有请求处理为一个
线程,但你可以做丰富的电子邮件
具有更高线程的通知
计数,甚至更好的过程
分析正在发生的事情或
我将在你的程序中的步骤
在另一篇文章中证明这一点
一个已经很长了。
现在我将把这个留给你,
享受..如果有的话发表评论
任何人们被困住的东西。
克里斯