You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi DataSnap WebModule变量修改异常:ServerMethodsUnit函数问题求助

解决DataSnap ServerMethods修改WebModule变量的异常问题

兄弟,我之前在做DataSnap Rest服务的时候也踩过类似的坑,咱们来一步步捋清楚问题出在哪,怎么解决:

核心问题根源

首先得明确:DataSnap的ServerMethods类默认是每个请求实例化一次的,而WebModule在默认配置下是单例模式,但直接访问WebModule的变量很容易触发线程安全问题,或者因为实例获取错误抛出异常。


第一步:检查变量声明是否正确

你提到在TWebModule1类里加了var1:string,一定要确认它是类的成员变量,而不是单元的全局变量。正确的声明位置应该是在类的private/protected/public区块里,比如:

TWebModule1 = class(TWebModule)
  DSHTTPService: TDSHTTPService;
  DSServer: TDSServer;
  DSServerClass1: TDSServerClass;
private
  { Private declarations }
  var1: string; // ✅ 放在类的私有区块里
public
  { Public declarations }
end;

如果把var1声明在单元的全局区域(比如var WebModule1: TWebModule1;下面),虽然能访问,但多请求场景下会出现数据混乱,甚至内存异常。


第二步:正确获取WebModule实例并修改变量

不要直接用WebModule1.var1这种方式访问!因为在多线程环境下,WebModule的实例可能不是你默认声明的那个全局变量。正确的做法是通过WebModuleClass获取当前请求对应的WebModule实例,同时必须处理线程安全:

在ServerMethodsUnit里创建两个函数:

function TServerMethods1.SetWebModuleVar1(const NewValue: string): Boolean;
var
  CurrentWebModule: TWebModule1;
begin
  Result := False;
  // 获取当前请求关联的WebModule实例
  CurrentWebModule := WebModuleClass as TWebModule1;
  if Assigned(CurrentWebModule) then
  begin
    // 用TMonitor保护变量读写,避免多线程冲突
    TMonitor.Enter(CurrentWebModule);
    try
      CurrentWebModule.var1 := NewValue;
      Result := True;
    finally
      TMonitor.Exit(CurrentWebModule);
    end;
  end;
end;

function TServerMethods1.GetWebModuleVar1: string;
var
  CurrentWebModule: TWebModule1;
begin
  Result := '';
  CurrentWebModule := WebModuleClass as TWebModule1;
  if Assigned(CurrentWebModule) then
  begin
    TMonitor.Enter(CurrentWebModule);
    try
      Result := CurrentWebModule.var1;
    finally
      TMonitor.Exit(CurrentWebModule);
    end;
  end;
end;

第三步:排查常见异常原因

如果还是出现异常,大概率是这几个问题:

  • 空指针访问:WebModuleClass返回nil,或者强制类型转换失败。检查DSServerClass的OnGetClass事件,确认绑定的是TServerMethods1类。
  • 线程安全冲突:多个请求同时读写var1,导致内存错误。上面的代码已经用TMonitor解决了这个问题,一定要保留。
  • 权限问题:如果var1声明为private,ServerMethodsUnit里的类无法访问。可以把它改成public,或者给WebModule加个封装属性(更优雅的做法)。

推荐:更优雅的封装方式

为了减少耦合,推荐给WebModule封装一个属性,把线程安全逻辑放在WebModule内部:

在WebModuleUnit1里:

TWebModule1 = class(TWebModule)
  DSHTTPService: TDSHTTPService;
  DSServer: TDSServer;
  DSServerClass1: TDSServerClass;
private
  FVar1: string;
  FCriticalSection: TCriticalSection;
  function GetVar1: string;
  procedure SetVar1(const Value: string);
protected
  procedure WebModuleCreate(Sender: TObject); override;
  procedure WebModuleDestroy(Sender: TObject); override;
public
  property Var1: string read GetVar1 write SetVar1;
end;

// 实现部分
procedure TWebModule1.WebModuleCreate(Sender: TObject);
begin
  FCriticalSection := TCriticalSection.Create;
end;

procedure TWebModule1.WebModuleDestroy(Sender: TObject);
begin
  FCriticalSection.Free;
end;

function TWebModule1.GetVar1: string;
begin
  FCriticalSection.Enter;
  try
    Result := FVar1;
  finally
    FCriticalSection.Leave;
  end;
end;

procedure TWebModule1.SetVar1(const Value: string);
begin
  FCriticalSection.Enter;
  try
    FVar1 := Value;
  finally
    FCriticalSection.Leave;
  end;
end;

然后在ServerMethodsUnit里调用就简单多了:

function TServerMethods1.SetWebModuleVar1(const NewValue: string): Boolean;
var
  CurrentWM: TWebModule1;
begin
  Result := False;
  CurrentWM := WebModuleClass as TWebModule1;
  if Assigned(CurrentWM) then
  begin
    CurrentWM.Var1 := NewValue;
    Result := True;
  end;
end;

function TServerMethods1.GetWebModuleVar1: string;
var
  CurrentWM: TWebModule1;
begin
  Result := '';
  CurrentWM := WebModuleClass as TWebModule1;
  if Assigned(CurrentWM) then
    Result := CurrentWM.Var1;
end;

这样既保证了线程安全,又符合面向对象的设计原则,后续维护也更方便。

内容的提问来源于stack exchange,提问作者Romuald Kler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:50:54