gpt4 book ai didi

multithreading - 以多线程方式使用Delphi7 COM接口(interface)时的内存消耗

转载 作者:行者123 更新时间:2023-12-03 15:11:19 26 4
gpt4 key购买 nike

在 Delphi7 中访问 COM 对象接口(interface)(例如 IXMLDocumentIXMLNode 等)时,似乎存在一些内存问题 -多线程的方式。其他COM接口(interface)可能也有这个问题,但我的“研究”并不是那么深入,因为我也必须继续我当前的项目。在单线程上创建 TXMLDocument 并通过诸如 IXMLDocumentIXMLNode 之类的接口(interface)操作它是可以的,但在多线程方法中,当一个线程创建TXMLDocument 对象和其他操作它使用越来越多的内存。 CoInitializeEx(nil, COINIT_MULTITHREADED) 在每个线程中都会被调用,但徒劳无功。似乎每个线程在获取接口(interface)时都会分配一些内存并且不会释放它,但每个线程都会分配一次 - 至少对于某个接口(interface) - 例如DocumentElementChildNodes - 因此除了创建对象的线程之外还有一个工作线程 - 不会导致可见内存泄漏。但动态创建的线程的行为方式都相同,最终会耗尽进程内存。

这是我的完整测试应用程序 Delphi7 form 作为 SCCE,它尝试显示上述三种不同的场景 - 单线程、一个工作线程和动态创建的线程。

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表单,而更少是因为你不能仅仅复制和编译它。这也是 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。非常欢迎任何帮助。

最佳答案

您正在使用自由线程的线程模型。当您调用TXMLDocument.Create 时,您将创建一个COM 对象。然后,您可以从多个线程使用该对象,而无需任何同步。换句话说,您违反了 COM 线程规则。可能还有比这更多的问题,但是在解决这个问题之前您不能指望继续下去。

关于multithreading - 以多线程方式使用Delphi7 COM接口(interface)时的内存消耗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19673614/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com