Multithreading 在Delphi中使用线程队列时,如何确保释放线程?
我再次,希望有更具体的线程问题 我注意到,如果我在这里运行Chris Rolliston提供的优秀演示: 打开FastMM以报告内存泄漏时,线程本身也会泄漏 这对于一个小的演示来说并没有问题,但对于我的应用程序来说,使用线程的迭代次数达到了数万次,它会耗尽内存来运行我那不起眼的32位应用程序。(我无法编译64位,因为我使用的是仅32位的串扰) 当与线程队列一起使用时,如何确保线程被释放 新增代码Multithreading 在Delphi中使用线程队列时,如何确保释放线程?,multithreading,delphi,Multithreading,Delphi,我再次,希望有更具体的线程问题 我注意到,如果我在这里运行Chris Rolliston提供的优秀演示: 打开FastMM以报告内存泄漏时,线程本身也会泄漏 这对于一个小的演示来说并没有问题,但对于我的应用程序来说,使用线程的迭代次数达到了数万次,它会耗尽内存来运行我那不起眼的32位应用程序。(我无法编译64位,因为我使用的是仅32位的串扰) 当与线程队列一起使用时,如何确保线程被释放 新增代码 program SimpleThreadQueueConsole; {$APPTYPE CONS
program SimpleThreadQueueConsole;
{$APPTYPE CONSOLE}
{$R *.res}
uses
FastMM4,
System.SysUtils,
System.SyncObjs,
uPrimeThread in 'uPrimeThread.pas',
uPrimeThreadRunner in 'uPrimeThreadRunner.pas';
var
PR: TPrimeThreadRunner;
begin
Randomize;
ReportMemoryLeaksOnShutdown := True;
PR := TPrimeThreadRunner.Create;
try
PR.DoIt;
finally
PR.Free;
end;
end.
uPrimeThread.pas
unit uPrimeThread;
interface
uses
System.Classes
, Generics.Collections
;
type
TPrimeThread = class(TThread)
private
FOutQueue: TThreadedQueue<string>;
FInQueue: TThreadedQueue<string>;
function IsPrime(const NumberToCheck: integer): boolean;
public
constructor Create(aCreateSuspended: Boolean; aInQueue: TThreadedQueue<string>; aOutQueue: TThreadedQueue<string>);
procedure Execute; override;
end;
implementation
uses
System.SysUtils
, System.SyncObjs
;
const
MaxPrime = 999;
{ TPrimeThread }
constructor TPrimeThread.Create(aCreateSuspended: Boolean; aInQueue, aOutQueue: TThreadedQueue<string>);
begin
inherited Create(aCreateSuspended);
FOutQueue := aOutQueue;
FInQueue := aInQueue;
FreeOnTerminate := True;
end;
procedure TPrimeThread.Execute;
var
S: string;
ThreadID: TThreadID;
NumberToCheck: integer;
begin
ThreadID := TThread.CurrentThread.ThreadID;
FOutQueue.PushItem(Format('Thread %d started...', [ThreadID]));
while (FInQueue.PopItem(S) = wrSignaled) do
begin
NumberToCheck := Random(MaxPrime);
if IsPrime(NumberToCheck) then
begin
FOutQueue.PushItem(Format('%s using thread %d: %d is prime', [S, ThreadID, NumberToCheck]));
end else
begin
FOutQueue.PushItem(Format('%s using thread %d: %d is NOT prime', [S, ThreadID, NumberToCheck]));
end;
end;
end;
function TPrimeThread.IsPrime(const NumberToCheck: Integer): boolean;
// This is really bad on purpose to make the threads work a little harder
var
i: integer;
begin
Result := True;
if NumberToCheck in [0, 1] then
begin
Result := False;
Exit;
end;
for i := 2 to NumberToCheck - 1 do
begin
if NumberToCheck mod i = 0 then
begin
Result := False;
Exit;
end;
end;
end;
end.
unituprimethread;
接口
使用
系统、类
,泛型。集合
;
类型
TPrimeThread=class(TThread)
私有的
FOutQueue:TThreadedQueue;
FInQueue:TThreadedQueue;
函数IsPrime(常量NumberToCheck:integer):布尔;
公众的
构造函数创建(aCreateSuspended:Boolean;aInQueue:TThreadedQueue;aOutQueue:TThreadedQueue);
程序执行;推翻
结束;
实施
使用
System.SysUtils
,System.SyncObjs
;
常数
MaxPrime=999;
{TPrimeThread}
构造函数TPrimeThread.Create(aCreateSuspended:Boolean;aInQueue,aOutQueue:TThreadedQueue);
开始
继承的创建(aCreateSuspended);
FOutQueue:=aOutQueue;
FInQueue:=aInQueue;
FreeOnTerminate:=真;
结束;
过程TPrimeThread.Execute;
变量
S:字符串;
ThreadID:TThreadID;
NumberToCheck:整数;
开始
ThreadID:=TThread.CurrentThread.ThreadID;
PushItem(格式('Thread%d started…',[ThreadID]);
而(FInQueue.PopItem)=wrSignaled)do
开始
NumberToCheck:=随机(MaxPrime);
如果是iPrime(数字检查),则
开始
PushItem(格式('%s使用线程%d:%d为prime',[s,线程ID,NumberToCheck]);
结束其他
开始
PushItem(格式(“%s”使用线程%d:%d不是prime,[s,ThreadID,NumberToCheck]);
结束;
结束;
结束;
函数TPrimeThread.IsPrime(常量NumberToCheck:Integer):布尔;
//这是非常糟糕的故意使线程工作更努力
变量
i:整数;
开始
结果:=真;
如果Number签入[0,1],则
开始
结果:=假;
出口
结束;
对于i:=2到数字检查-1 do
开始
如果NumberToCheck mod i=0,则
开始
结果:=假;
出口
结束;
结束;
结束;
结束。
uPrimeThreadRunner.pas
unit uPrimeThreadRunner;
interface
uses
System.SyncObjs
, Generics.Collections
, System.SysUtils
, uPrimeThread
;
const
ThreadCount = 4;
type
TPrimeThreadRunner = class
private
FTotalThreads: TCountdownEvent;
FInQueue, FOutQueue: TThreadedQueue<string>;
FCurrentEntry: integer;
procedure DrainTheQueue;
procedure AddEntry;
public
ThreadArray: array[1..ThreadCount] of TPrimeThread;
procedure DoIt;
end;
implementation
const
NumberOfEntries = 10;
procedure TPrimeThreadRunner.DrainTheQueue;
var
S: string;
begin
while FOutQueue.PopItem(S) = wrSignaled do
WriteLn(S);
end;
procedure TPrimeThreadRunner.AddEntry;
var
S: string;
begin
Inc(FCurrentEntry);
S := Format('Entry %d:', [FCurrentEntry]) ;
FInQueue.PushItem(S);
end;
procedure TPrimeThreadRunner.DoIt;
var
i: integer;
begin
FCurrentEntry := 0;
FTotalThreads := TCountdownEvent.Create(1);
FInQueue := TThreadedQueue<string>.Create(10, 1000, 1000);
FOutQueue := TThreadedQueue<string>.Create(10, 1000, 1000);
for i := 1 to ThreadCount do
begin
FTotalThreads.AddCount;
try
ThreadArray[i] := TPrimeThread.Create(True, FInQueue, FOutQueue);
ThreadArray[i].Start;
finally
FTotalThreads.Signal;
end;
end;
for I := 1 to NumberOfEntries do
begin
AddEntry;
end;
DrainTheQueue;
FTotalThreads.Signal;
FTotalThreads.WaitFor;
FTotalThreads.Free;
FInQueue.Free;
FOutQueue.Free;
Readln;
end;
end.
unitupriturerunner;
接口
使用
System.SyncObjs
,泛型。集合
,System.SysUtils
,向上螺纹
;
常数
线程数=4;
类型
tpromethreadrunner=class
私有的
FTotalThreads:TCountdownEvent;
FInQueue,FOutQueue:TThreadedQueue;
FCurrenentry:整数;
程序队列;
程序补遗;
公众的
ThreadArray:Tprimeread的数组[1..ThreadCount];
程序DoIt;
结束;
实施
常数
NumberOfEntries=10;
程序TPrimeThreadRunner.DrainTheQueue;
变量
S:字符串;
开始
而FOutQueue.PopItem=wrSignaled do
书面文件(S);;
结束;
程序TPrimeThreadRunner.AddEntry;
变量
S:字符串;
开始
公司(FCurrenentry);
S:=格式('条目%d:',[fcurrenentry]);
FInQueue.PushItem;
结束;
程序TPrimeThreadRunner.DoIt;
变量
i:整数;
开始
FCurrenentry:=0;
FTotalThreads:=TCountdownEvent.Create(1);
FInQueue:=TThreadedQueue.Create(10,1000,1000);
FOutQueue:=TThreadedQueue.Create(10,1000,1000);
对于i:=1到ThreadCount do
开始
FTotalThreads.AddCount;
尝试
ThreadArray[i]:=TPrimeThread.Create(True、FInQueue、FOutQueue);
ThreadArray[i].Start;
最后
FTotalThreads.信号;
结束;
结束;
对于I:=1到NumberOfEntries do
开始
补遗;
结束;
排龙;
FTotalThreads.信号;
FTotalThreads.WaitFor;
免费;
FInQueue.Free;
免费的;
Readln;
结束;
结束。
您的代码中有一个错误,我可以在XE3和XE6中复制它
关键错误如下:
for I := 1 to NumberOfEntries do
begin
AddEntry;
end;
DrainTheQueue; // <-- All threads may not be ready when this call finish
FTotalThreads.Signal; // This makes nothing
FTotalThreads.WaitFor; // This makes nothing
FTotalThreads.Free; // This makes nothing
FInQueue.Free; // You may now free a queue in operation
FOutQueue.Free; // You may now free a queue in operation
并更改PrimeThread。执行如下操作:
procedure TPrimeThread.Execute;
var
S: string;
ThreadID: TThreadID;
NumberToCheck: integer;
begin
ThreadID := TThread.CurrentThread.ThreadID;
FOutQueue.PushItem(Format('Thread %d started...', [ThreadID]));
try
while NOT Terminated do
begin
if (FInQueue.PopItem(S) = wrSignaled) then
begin
if (S = '') then // Stop executing
Exit;
NumberToCheck := Random(MaxPrime);
if IsPrime(NumberToCheck) then
begin
FOutQueue.PushItem(Format('%s using thread %d: %d is prime', [S, ThreadID, NumberToCheck]));
end else
begin
FOutQueue.PushItem(Format('%s using thread %d: %d is NOT prime', [S, ThreadID, NumberToCheck]));
end;
end;
end;
finally
FOutQueue.PushItem(Format('Thread %d ended ...', [ThreadID]));
end;
end;
您还应该将FTotalThreads
对象传递给工作线程,并让它们在完成执行时发出信号。
draintequeue
必须在FTotalThreads.Waitfor
完成后释放线程。使用线程队列?到底是怎么回事?生产者/消费者?向使用者发送终止作业。完成后释放它。我使用的线程队列与上面链接的演示完全相同。事实上,就像现在一样,它会泄漏线程。如果我在通话结束后拨打免费电话,则应用程序会锁定呼叫免费。非网站链接。为什么代码不能在这里?必要时简化。通常,您会向队列发出终止的信号,然后所有消费者都会通过退出来响应该信号。等待他们全部收到信号。也许是通过破坏它们。是的,我猜线程中的循环不会检查线程是否被终止。除非您显式退出循环或强制终止线程(这是不建议的),否则该循环将一直持续下去。示例代码中的匿名工作线程会吐出状态消息-您是否看到线程%d正在终止
消息?默认情况下,匿名线程是FreeOnTerminate
线程。如果他们没有被释放,那是因为他们还没有完成工作。如果是这样的话,那么这个问题应该是:为什么我的工作线程被卡住了?是的!非常感谢你。这解决了我的问题
procedure TPrimeThread.Execute;
var
S: string;
ThreadID: TThreadID;
NumberToCheck: integer;
begin
ThreadID := TThread.CurrentThread.ThreadID;
FOutQueue.PushItem(Format('Thread %d started...', [ThreadID]));
try
while NOT Terminated do
begin
if (FInQueue.PopItem(S) = wrSignaled) then
begin
if (S = '') then // Stop executing
Exit;
NumberToCheck := Random(MaxPrime);
if IsPrime(NumberToCheck) then
begin
FOutQueue.PushItem(Format('%s using thread %d: %d is prime', [S, ThreadID, NumberToCheck]));
end else
begin
FOutQueue.PushItem(Format('%s using thread %d: %d is NOT prime', [S, ThreadID, NumberToCheck]));
end;
end;
end;
finally
FOutQueue.PushItem(Format('Thread %d ended ...', [ThreadID]));
end;
end;