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
相关产品推荐
相关产品推荐

