Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading DELPHI:多线程客户端/服务器数据捕捉错误_Multithreading_Delphi_Datasnap - Fatal编程技术网

Multithreading DELPHI:多线程客户端/服务器数据捕捉错误

Multithreading DELPHI:多线程客户端/服务器数据捕捉错误,multithreading,delphi,datasnap,Multithreading,Delphi,Datasnap,这是我在这里的第一篇帖子-所以要温柔:-) 我想构建一个使用datasnap进行数据传输的客户机/服务器应用程序。 这是一项相当简单的任务,有很多例子可以学习。 但是,拥有一个Datasnap服务器(从Delphi XE向导构建)我发现自己遇到了一个问题,我希望有人能引导我走向正确的方向 服务器和客户端在同一台PC上运行(这是目前的设计)。 服务器正在运行会话生命周期。 服务器和客户端共享一个类(发布在下面) 服务器提供了一个简单的方法——GetServerObject,它使用GetNewObj

这是我在这里的第一篇帖子-所以要温柔:-)

我想构建一个使用datasnap进行数据传输的客户机/服务器应用程序。 这是一项相当简单的任务,有很多例子可以学习。 但是,拥有一个Datasnap服务器(从Delphi XE向导构建)我发现自己遇到了一个问题,我希望有人能引导我走向正确的方向

服务器和客户端在同一台PC上运行(这是目前的设计)。 服务器正在运行会话生命周期。 服务器和客户端共享一个类(发布在下面)

服务器提供了一个简单的方法——GetServerObject,它使用GetNewObject方法。 服务器本身是一个VCL应用程序-主要形式是fmServer。 OnCreate将恢复Servers FormObject属性(FormObject:=TMyDataObject.Create)

所有这些都是非常简单的——只有当我将客户端应用程序扭曲成一个多线程怪物时,我的问题才会出现:-)(读取-超过1个线程)

这是客户端的线程代码

  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.