Multithreading DELPHI:多线程客户端/服务器数据捕捉错误
这是我在这里的第一篇帖子-所以要温柔:-) 我想构建一个使用datasnap进行数据传输的客户机/服务器应用程序。 这是一项相当简单的任务,有很多例子可以学习。 但是,拥有一个Datasnap服务器(从Delphi XE向导构建)我发现自己遇到了一个问题,我希望有人能引导我走向正确的方向 服务器和客户端在同一台PC上运行(这是目前的设计)。 服务器正在运行会话生命周期。 服务器和客户端共享一个类(发布在下面) 服务器提供了一个简单的方法——GetServerObject,它使用GetNewObject方法。 服务器本身是一个VCL应用程序-主要形式是fmServer。 OnCreate将恢复Servers FormObject属性(FormObject:=TMyDataObject.Create) 所有这些都是非常简单的——只有当我将客户端应用程序扭曲成一个多线程怪物时,我的问题才会出现:-)(读取-超过1个线程) 这是客户端的线程代码Multithreading DELPHI:多线程客户端/服务器数据捕捉错误,multithreading,delphi,datasnap,Multithreading,Delphi,Datasnap,这是我在这里的第一篇帖子-所以要温柔:-) 我想构建一个使用datasnap进行数据传输的客户机/服务器应用程序。 这是一项相当简单的任务,有很多例子可以学习。 但是,拥有一个Datasnap服务器(从Delphi XE向导构建)我发现自己遇到了一个问题,我希望有人能引导我走向正确的方向 服务器和客户端在同一台PC上运行(这是目前的设计)。 服务器正在运行会话生命周期。 服务器和客户端共享一个类(发布在下面) 服务器提供了一个简单的方法——GetServerObject,它使用GetNewObj
TDataThread = class(TThread)
private
DSConn: TSQLConnection;
protected
procedure Execute; override;
public
constructor Create(aConn: TSQLConnection); overload;
end;
constructor TDataThread.Create(aConn: TSQLConnection);
begin
inherited Create(False);
DSConn := aConn.CloneConnection;
FreeOnTerminate := true;
end;
procedure TDataThread.Execute;
var
DSMethod: TServerMethods2Client;
aDataObject : TMyDataObject;
begin
NameThreadForDebugging('Data');
{ Place thread code here }
DSMethod := nil;
try
while not terminated do
begin
sleep(10);
if DSConn.Connected then
begin
try
if DSMethod = nil then
DSMethod := TServerMethods2Client.Create(DSConn.DBXConnection,false);
if DSMethod <> nil then
try
aDataObject := DSMethod.GetserverObject;
finally
freeandnil(aDataObject);
end;
except
freeandnil(DSMethod);
DSConn.Connected := False;
end
end
else
begin
// connect
try
sleep(100);
DSConn.Open;
except
;
end;
end;
end;
finally
freeandnil(DSMethod);
DSConn.Close;
freeandnil(DSConn);
end;
TDataThread=class(TThread)
私有的
DSConn:TSQLConnection;
受保护的
程序执行;推翻
公众的
构造函数创建(acon:TSQLConnection);超载;
结束;
构造函数TDataThread.Create(acon:TSQLConnection);
开始
继承创建(False);
DSConn:=A克隆连接;
FreeOnTerminate:=真;
结束;
过程TDataThread.Execute;
变量
DSMethod:TServerMethods2Client;
AdaaObject:TMyDataObject;
开始
NameThreadForDebugging(“数据”);
{在此处放置线程代码}
d方法:=零;
尝试
虽然没有终止
开始
睡眠(10);
如果DSConn.已连接,则
开始
尝试
如果DSMethod=nil,则
DSMethod:=TServerMethods2Client.Create(DSConn.DBXConnection,false);
如果方法为零,则
尝试
aDataObject:=DSMethod.GetserverObject;
最后
freeandnil(aDataObject);
结束;
除了
freeandnil(DSMethod);
DSConn.Connected:=错误;
结束
结束
其他的
开始
//连接
尝试
睡眠(100);
DSConn.Open;
除了
;
结束;
结束;
结束;
最后
freeandnil(DSMethod);
DSConn.Close;
freeandnil(DSConn);
结束;
当我创建一个以上的线程时,最终我会得到一个错误(即“无法稳定…”或一些“远程dbx错误…”等等
我根本无法实现这一点——因此我可以生成数百个到datasnap服务器的线程/连接
我知道这个问题很棘手,但我希望有人比我聪明:-)
如果我尝试相同的客户机线程代码,但访问一个更简单的服务器方法(比方说样本中的echostring),那么我可以用数百个线程运行它。
也许我在这里回答我自己——但我太盲目了,没有意识到这一点:——)
单位uDataObject;
接口
使用
SysUtils;
类型
TMyDataObject=类(TObject)
私有的
fString:String;
fInteger:整数;
公众的
构造函数创建;事实上的
毁灭者毁灭;推翻
函数赋值(aSource:TMyDataObject):布尔值;
属性aString:字符串读取fString写入fString;
属性aInteger:整数读取fInteger写入fInteger;
结束;
实施
{TMyDataObject}
函数TMyDataObject.Assign(aSource:TMyDataObject):布尔;
开始
如果资源为零,则
开始
尝试
fString:=a来源:aString;
fInteger:=aSource.aInteger;
结果:=真;
除了
结果:=假;
结束;
结束
其他的
结果:=假;
结束;
构造函数TMyDataObject.Create;
开始
继承;
随机化;
fString:=“创建时间为:”+FormatDateTime('ddmmyyyyy hh:nn:ss:zzz',现在);
fInteger:=随机(100);
结束;
析构函数TMyDataObject.Destroy;
开始
继承;
结束;
结束。
非常感谢所有帮助当简单的服务器方法起作用时,我认为您的问题必须在“真正的”代码正在执行或使用的某个地方找到 它可能在连接中(尝试更改更简单的代码以使用连接) 您的问题也可以是CloneConnection。当克隆的连接从中被释放时,克隆的连接被释放。看见
这一点在评论和bug报告中得到了大部分回答,但是。。。您看到的问题是由XE的封送器代码中的多线程问题引起的。如果两个线程(或两个客户端)调用一个服务器-服务器方法,该方法同时接收或返回用户定义的类型(任何将使用封送拆收器/解封送器的类型),则可能会发生异常 我不知道XE有一个完美的解决方案,但是如果可以不使用用户定义的类型,那么您就不应该看到多线程问题
Mat可能不相关,但我注意到您的
TDataThread
构造函数调用了继承的Create(False)
,它将在克隆连接之前在新线程的上下文中立即运行Execute
方法。@TOndrej:不相关或不相关,这是个问题。我们已将创建所有挂起线程作为一项策略。事实上,我们的TThread子代的构造函数不允许使用“CreateSuspended”参数,甚至不允许调用普通构造函数(引发异常)…您不应该在构造函数中调用Randomize。在应用程序启动期间,只需调用一次随机化即可。@Marjan Venema有趣的是,我通常在调用继承的构造函数之前初始化线程实例数据,而不是suspended.Hi。我现在调用onformcreate事件中的随机化。创建挂起的线程。我只是希望线程尽快运行——所以我调用resume作为构造函数中的最后一条语句——这给了我一个关于弃用的警告。但弃用与“不工作”不同:-),但它仍然抛出“无效指针操作…”。。。并在客户处打断客户
TDataThread = class(TThread)
private
DSConn: TSQLConnection;
protected
procedure Execute; override;
public
constructor Create(aConn: TSQLConnection); overload;
end;
constructor TDataThread.Create(aConn: TSQLConnection);
begin
inherited Create(False);
DSConn := aConn.CloneConnection;
FreeOnTerminate := true;
end;
procedure TDataThread.Execute;
var
DSMethod: TServerMethods2Client;
aDataObject : TMyDataObject;
begin
NameThreadForDebugging('Data');
{ Place thread code here }
DSMethod := nil;
try
while not terminated do
begin
sleep(10);
if DSConn.Connected then
begin
try
if DSMethod = nil then
DSMethod := TServerMethods2Client.Create(DSConn.DBXConnection,false);
if DSMethod <> nil then
try
aDataObject := DSMethod.GetserverObject;
finally
freeandnil(aDataObject);
end;
except
freeandnil(DSMethod);
DSConn.Connected := False;
end
end
else
begin
// connect
try
sleep(100);
DSConn.Open;
except
;
end;
end;
end;
finally
freeandnil(DSMethod);
DSConn.Close;
freeandnil(DSConn);
end;
unit uDataObject;
interface
uses
SysUtils;
Type
TMyDataObject = class(TObject)
private
fString: String;
fInteger: Integer;
public
constructor Create; virtual;
destructor Destroy; override;
function Assign(aSource: TMyDataObject): boolean;
property aString: String read fString write fString;
property aInteger: Integer read fInteger write fInteger;
end;
implementation
{ TMyDataObject }
function TMyDataObject.Assign(aSource: TMyDataObject): boolean;
begin
if aSource <> nil then
begin
try
fString := aSource.aString;
fInteger := aSource.aInteger;
Result := True;
except
Result := false;
end;
end
else
Result := false;
end;
constructor TMyDataObject.Create;
begin
inherited;
Randomize;
fString := 'The time of creation is : ' + FormatDateTime('ddmmyyyy hh:nn:ss:zzz', Now);
fInteger := Random(100);
end;
destructor TMyDataObject.Destroy;
begin
inherited;
end;
end.