在以多线程方式访问COM
对象接口(例如IXMLDocument
等等)时,Delphi7 中似乎存在一些内存问题。IXMLNode
其他COM interfaces
人可能会分享这个问题,但我的“研究”并不是那么深入,因为我也必须继续我目前的项目。通过像和在单个线程上这样的接口创建TXMLDocument
和操作它是可以的,但是在多线程方法中,当一个线程创建对象而其他线程操作它时,它会使用越来越多的内存。在每个线程中都被调用但徒劳无功。似乎每个线程在获取接口时都会分配一些内存并且不会释放它,但是每个线程都会分配一次 - 至少对于某个接口 - 例如或IXMLDocument
IXMLNode
TXMLDocument
CoInitializeEx(nil, COINIT_MULTITHREADED)
DocumentElement
ChildNodes
- 所以创建对象的工作线程旁边的一个工作线程 - 不会导致可见的内存泄漏。但是动态创建的线程的行为方式都是相同的,最终会消耗掉进程内存。
这是我作为 SCCE 的完整测试应用程序 Delphi7 form
,它尝试显示上述三种不同的场景——单线程、一个工作线程和动态创建的线程。
unit uComTest;
interface
uses
Windows, SysUtils, Classes, Forms, ExtCtrls, Controls, StdCtrls, XMLDoc, XMLIntf, ActiveX;
type
TMyThread = class(TThread)
procedure Execute;override;
end;
TForm1 = class(TForm)
btnMainThread: TButton;
edtText: TEdit;
Timer1: TTimer;
btnOneThread: TButton;
btnMultiThread: TButton;
Timer2: TTimer;
chkXMLUse: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure btnMainThreadClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnOneThreadClick(Sender: TObject);
procedure btnMultiThreadClick(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
fXML:TXMLDocument;
fXMLDocument:IXMLDocument;
fThread:TMyThread;
fCount:Integer;
fLoop:Boolean;
procedure XMLCreate;
function XMLGetItfc:IXMLDocument;
procedure XMLUse;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
CoinitializeEx(nil, COINIT_MULTITHREADED);
XMLCreate; //XML is created on MainThread;
Timer1.Enabled := false;
Timer2.Enabled := false;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
fIXMLDocument := nil;
CoUninitialize;
end;
procedure TForm1.XMLCreate;
begin
fXML := TXMLDocument.Create('.\try.xml');
fXML.Active;
fXML.GetInterface(IXMLDocument, fIXMLDocument);
end;
function TForm1.XMLGetItfc:IXMLDocument;
begin
fXML.GetInterface(IXMLDocument, Result);
end;
procedure TForm1.XMLUse;
begin
Inc(fCount);
if chkXMLUse.Checked then
begin
XMLGetItfc.DocumentElement;
edtText.Text := IntToStr(GetCurrentThreadId) + ': ' + 'XML access ' + IntToStr(fCount);
end
else
edtText.Text := IntToStr(GetCurrentThreadId) + ': ' + 'NO XML access ' + IntToStr(fCount)
end;
procedure TForm1.btnMainThreadClick(Sender: TObject);
begin
fCount := 0;
fLoop := false;
Timer1.Enabled := not Timer1.Enabled;
end;
procedure TForm1.btnOneThreadClick(Sender: TObject);
begin
if fLoop then
fLoop := false
else
begin
fCount := 0;
fLoop := true;
fThread := TMyThread.Create(FALSE);
end;
end;
procedure TForm1.btnMultiThreadClick(Sender: TObject);
begin
fCount := 0;
fLoop := false;
Timer2.Enabled := not Timer2.Enabled;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
XMLUse;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
begin
TMyThread.Create(FALSE);
end;
//this procedure executes in every thread
procedure TMyThread.Execute;
begin
FreeOnTerminate := TRUE;
CoinitializeEx(nil, COINIT_MULTITHREADED);
try
repeat
Form1.XMLUse;
if Form1.floop then
sleep(100);
until not Form1.floop;
finally
CoUninitialize;
end;
end;
end.
好吧,这是非常必要的,因为它是一个可以工作的 Delphi 表单,带有buttons
andtimers
和 less,因为你不能只是复制和编译它。这里也是form
的 dfm:
object Form1: TForm1
Left = 54
Top = 253
Width = 337
Height = 250
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 13
object btnMainThread: TButton
Left = 24
Top = 32
Width = 75
Height = 25
Caption = 'MainThread'
TabOrder = 0
OnClick = btnMainThreadClick
end
object edtText: TEdit
Left = 24
Top = 8
Width = 257
Height = 21
TabOrder = 1
end
object btnOneThread: TButton
Left = 24
Top = 64
Width = 75
Height = 25
Caption = 'One Thread'
TabOrder = 2
OnClick = btnOneThreadClick
end
object btnMultiThread: TButton
Left = 24
Top = 96
Width = 75
Height = 25
Caption = 'MultiThread'
TabOrder = 3
OnClick = btnMultiThreadClick
end
object chkXMLUse: TCheckBox
Left = 112
Top = 88
Width = 97
Height = 17
Caption = 'XML use'
Checked = True
State = cbChecked
TabOrder = 4
end
object Timer1: TTimer
Interval = 100
OnTimer = Timer1Timer
end
object Timer2: TTimer
Interval = 100
OnTimer = Timer2Timer
Left = 32
end
end
这是一个控制台应用程序。只需运行它,看看是否发生任何内存消耗。如果您认为可以编写一种保留多线程但不占用内存的方式,请随意修改它:
program ConsoleTest;
{$APPTYPE CONSOLE}
uses
Windows, SysUtils, Classes, XMLDoc, XMLIntf, ActiveX;
type
TMyThread = class(TThread)
procedure Execute;override;
end;
var
fCriticalSection:TRTLCriticalSection;
fIXMLDocument:IXMLDocument;
i:Integer;
//--------- Globals -------------------------------
procedure XMLCreate;
begin
fIXMLDocument := TXMLDocument.Create('.\try.xml');
fIXMLDocument.Active;
end;
procedure XMLUse;
begin
fIXMLDocument.DocumentElement;
end;
//------- TMyThread ------------------------------
procedure TMyThread.Execute;
begin
FreeOnTerminate := TRUE;
EnterCriticalSection(fCriticalSection);
try
CoinitializeEx(nil, COINIT_MULTITHREADED);
try
XMLUse;
finally
CoUninitialize;
end;
finally
LeaveCriticalSection(fCriticalSection);
end;
end;
//------------ Main -------------------------
begin
InitializeCriticalSection(fCriticalSection);
CoinitializeEx(nil, COINIT_MULTITHREADED);
try
XMLCreate;
try
for i := 0 to 100000 do
begin
TMyThread.Create(FALSE);
sleep(100);
end;
finally
fIXMLDocument := nil;
end;
finally
CoUninitialize;
DeleteCriticalSection(fCriticalSection);
end;
end.
我在 Windows7 上使用 Delphi7 Enterprise。非常欢迎任何帮助。