【发布时间】:2020-12-13 06:21:52
【问题描述】:
我有一个服务,我在主线程中存储一些数据并有时从子线程中读取它。
使用 Delphi 7 一切正常。
服务执行、子线程创建、主线程生成数据、子线程调用Synchronise获取数据……等到主线程ServiceThread.ProcessRequests(True);
现在使用 Delphi 10.3,Synchronise 似乎没有等待主线程到达 ProcessRequests(空闲)...它在主执行处理过程中调用。
主服务线程:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs;
type
TTestserv2 = class(TService)
procedure ServiceExecute(Sender: TService);
private
{ Private declarations }
procedure log(msg: String);
public
function GetServiceController: TServiceController; override;
function getArrayItem(i: integer): string;
{ Public declarations }
protected
function DoCustomControl(CtrlCode: Cardinal): Boolean; override;
end;
Const
SERVICE_CONTROL_MyMSG = 10;
var
Testserv2: TTestserv2;
implementation
{$R *.dfm}
Uses unit2;
Var
array1 : Array of string;
Thread1 : T_Thread1;
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
Testserv2.Controller(CtrlCode);
end;
function TTestserv2.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TTestserv2.log(msg: String);
Var
F:TextFile;
LogFile:String;
TmpStr:String;
begin
try
LogFile := 'c:\testlog1.txt';
AssignFile(F, LogFile);
If FileExists(LogFile) then
Append(F)
Else
Rewrite(F);
DateTimeToString(TmpStr,'yyyy.mm.dd. hh:nn:ss',now);
WriteLN(F,TmpStr+' - '+Msg);
Flush(F);
Finally
CloseFile(F);
End;
end;
function TTestserv2.DoCustomControl(CtrlCode: Cardinal): Boolean;
begin
result := true;
case CtrlCode of
SERVICE_CONTROL_MyMSG : log('MyMSG');
end;
end;
procedure TTestserv2.ServiceExecute(Sender: TService);
var
Msg: String;
i: integer;
s: string;
Begin
Log('Service Execute');
SetLength(array1, 20);
Thread1 := T_Thread1.Create;
Thread1.Priority:=tpNormal;
Thread1.Resume;
Log('Thread1 created');
// Where the magic happens
for i := 0 to 21 do
Begin
s := 'value='+ IntToStr( i*2);
array1[i] := s;
Log( IntToStr(i) + '-' + s);
sleep(100); // in real code some idSNMP query here
End;
while not Terminated do
begin
Sleep(50);
Log('Service Execute OK ');
If Terminated then
Log('Terminated');
ServiceThread.ProcessRequests(True);
end;
End;
function TTestserv2.getArrayItem(i:integer):string;
Begin
result := array1[i];
End;
end.
子线程:
unit unit2;
interface
uses
Windows, Classes, SysUtils, ExtCtrls, SyncObjs, ADODB, ActiveX, Unit1;
type
T_Thread1 = class(TThread)
private
{ Private declarations }
FWakeupEvent : TSimpleEvent;
procedure Log(Msg:String);
procedure Terminate1(Sender: TObject);
Procedure getdataproc;
protected
procedure Execute; override;
public
constructor Create;
Destructor Destroy; override;
end;
implementation
{ T_Thread1 }
constructor T_Thread1.Create;
begin
inherited Create(True);
OnTerminate := Terminate1;
FreeOnTerminate := False;
End;
procedure T_Thread1.Terminate1(Sender: TObject);
Var
s2:String;
begin
CoUninitialize;
End;
Destructor T_Thread1.Destroy;
Begin
If not Terminated Then Terminate;
inherited;
End;
procedure T_Thread1.log(msg: String);
Var
F:TextFile;
LogFile:String;
TmpStr:String;
begin
try
LogFile := 'c:\testlog2.txt';
AssignFile(F, LogFile);
If FileExists(LogFile) then
Append(F)
Else
Rewrite(F);
DateTimeToString(TmpStr,'hh:nn:ss',now);
WriteLN(F,TmpStr+' - '+Msg);
Flush(F);
Finally
CloseFile(F);
End;
end;
procedure T_Thread1.Execute;
Var
WaitStatus: Cardinal;
begin
LOG('Execute Start');
CoInitialize(nil);
FWakeupEvent := TSimpleEvent.Create;
repeat
WaitStatus := WaitForSingleObject(FWakeupEvent.Handle, 1000);
case WaitStatus of
WAIT_OBJECT_0: Break;
WAIT_TIMEOUT:
Begin
Log('Timeout');
Synchronize(getdataproc);
End;
Else Break;
end;
until (Terminated);
FreeAndNil(FWakeupEvent);
end;
Procedure T_Thread1.getdataproc;
Var
i:integer;
res:string;
Begin
for i := 0 to 21 do
Begin
res := Testserv2.getArrayItem(i);
log(IntToStr(i)+ '-' + res);
End;
End;
end.
结果
主日志:
16:27:01 - Service Execute
16:27:01 - Thread1 created
16:27:01 - 0-value=0
16:27:01 - 1-value=2
16:27:01 - 2-value=4
16:27:01 - 3-value=6
16:27:01 - 4-value=8
16:27:01 - 5-value=10
16:27:01 - 6-value=12
16:27:02 - 7-value=14
16:27:02 - 8-value=16
16:27:02 - 9-value=18
16:27:02 - 10-value=20
16:27:02 - 11-value=22
16:27:02 - 12-value=24
16:27:02 - 13-value=26
16:27:02 - 14-value=28
16:27:02 - 15-value=30
16:27:03 - 16-value=32
16:27:03 - 17-value=34
16:27:03 - 18-value=36
16:27:03 - 19-value=38
16:27:03 - 20-value=40
16:27:03 - 21-value=42
16:27:03 - Service Execute OK
子线程的log2:
16:27:01 - Execute Start
16:27:02 - Timeout
16:27:02 - 0-value=0
16:27:02 - 1-value=2
16:27:02 - 2-value=4
16:27:02 - 3-value=6
16:27:02 - 4-value=8
16:27:02 - 5-value=10
16:27:02 - 6-value=12
16:27:02 - 7-value=14
16:27:02 - 8-value=16
16:27:02 - 9-value=18
16:27:02 - 10-
16:27:02 - 11-
16:27:02 - 12-
16:27:02 - 13-
16:27:02 - 14-
16:27:02 - 15-
16:27:02 - 16-
16:27:02 - 17-
16:27:02 - 18-
16:27:02 - 19-
16:27:02 - 20-
16:27:02 - 21-
16:27:03 - Timeout
16:27:03 - 0-value=0
16:27:03 - 1-value=2
16:27:03 - 2-value=4
16:27:03 - 3-value=6
16:27:03 - 4-value=8
16:27:03 - 5-value=10
16:27:03 - 6-value=12
16:27:03 - 7-value=14
16:27:03 - 8-value=16
16:27:03 - 9-value=18
16:27:03 - 10-value=20
16:27:03 - 11-value=22
16:27:03 - 12-value=24
16:27:03 - 13-value=26
16:27:03 - 14-value=28
16:27:03 - 15-value=30
16:27:03 - 16-value=32
16:27:03 - 17-value=34
16:27:03 - 18-value=36
16:27:03 - 19-
16:27:03 - 20-
16:27:03 - 21-
16:27:04 - Timeout
16:27:04 - 0-value=0
16:27:04 - 1-value=2
16:27:04 - 2-value=4
16:27:04 - 3-value=6
16:27:04 - 4-value=8
16:27:04 - 5-value=10
16:27:04 - 6-value=12
16:27:04 - 7-value=14
16:27:04 - 8-value=16
16:27:04 - 9-value=18
16:27:04 - 10-value=20
16:27:04 - 11-value=22
16:27:04 - 12-value=24
16:27:04 - 13-value=26
16:27:04 - 14-value=28
16:27:04 - 15-value=30
16:27:04 - 16-value=32
16:27:04 - 17-value=34
16:27:04 - 18-value=36
16:27:04 - 19-value=38
16:27:04 - 20-value=40
16:27:04 - 21-value=42
所以对于前两轮,孩子在主循环的中间调用。
不等待。在实际代码中,数组是一个包含更多字符串和整数项的记录数组。
有时(非常非常罕见)结果是这样的:???†??????e se OK ?ô 像同步无法正常工作。 (编译成 32 位和 64 位,结果相同)
我能做什么?不推力同步?临界区?
不想重写一切。 子 PostThreadMessage CM_SERVICE_CONTROL_CODE 到 main,而 main PostThreadMessage 返回了更多数据(一些 kB)......我尽量避免。
有什么建议吗?
【问题讨论】:
-
IIRC Synchronize() 需要一个消息循环,因此只能用于 GUI 应用程序 (VCL/FMX)。它可能不会在服务中使用。
-
服务主线程默认有一个消息队列。我定期向它发送消息(使用 CM_SERVICE_CONTROL_CODE)。但是,此示例中的子线程(调用 Synchronize )没有消息队列。它需要一个吗?
-
@ArnaudBouchez
TThread.Synchronize()由项目的.dpr文件中的TServiceApplication.Run()中的主消息循环处理。它在服务中工作得很好,但它只是不与TService实际运行的线程一起使用,这不是实际的主线程。 -
@MrZed 不,子线程不需要自己的消息队列,除非您想将消息发回子线程。
标签: multithreading delphi service delphi-10.3-rio