客户端也会有类似报错:
原因:在最后一层,即:数据库连接层及事务处理层:DataBase Server,Socket:Address和IP套接字层:TFDConnection和TFDManager(两者要调用要唯一),TFDConnection和TFDManager同时处理连接,就会报错,比如:动态获取连接参数
procedure TServerMethods1.DataModuleCreate(Sender: TObject);
var iListcount:integer;
//触发时间:TWebModule1.DSServerClass1GetClass
begin
//初始化动态获取数据库连接参数:
if (FDConGlobal.ConnectionDefName).trim='' then
begin
FDConGlobal.Params.Clear;
FDConGlobal.ConnectionDefName := 'MSSQL';
end;
// ......此时,你要是同时,或在Appliaction Server的其它任何地方触发了类似操作 :
// FDManager1.GetConnectionDefParams('MSSQL',AList); //
if MainServerForm.myGetCatalogNames.Trim='' then
with MainServerForm do
begin
myGetCatalogNames:=FDConGlobal.Params.Database;
// ......FDManager1.GetCatalogNames等等
end;
end;
因而:在所有RestFul开发环境下,因为客户端是可以无状态的(取决于生命周期的设定方式),所有套接字问题,都是在分水岭TServerMethods1.DataModuleCreate事件触发的前后产生的!最底层的应用服务器以上的层次,是产生套接字问题的源泉。所谓套接字,指的是TCP的IP地址和Port端口,一次请求和响应的轮回,它们必须是唯一被使用的,其所有连接属性,不可以重复赋值或在其交互过程中被动态改变的,可以理解为其交互过程中它们必须是静态的。
if not Assigned(ADataSet) then
ADataSet:=
ClientModule1.ServerMethods1Client.getTFDDataSet(pSql,pSelectKey);
//:调用服务端方法被客户端组件引用时,处理方式很重要,
//:否则会报invalid Pionter错误!
关键: if not Assigned(ADataSet) then ; //在多核多处理器环境下,如果使用任务(线程池)或多线程并行,一定要注意!!!单核环境或win32应用不存在此问题!
并在客户端退出时,引发内存泄漏!
应用环境:在多核多处理器环境下,如果使用任务(线程池)或多线程并行。单核环境或win32应用不存在此问题!
正确方法:1、在任务代码中,使用TMonitor.Enter和TMonitor.Exit机制。(1)、在方法进入时:TMonitor.Enter(self,0); //多核多处理器环境,经常用uses system; (2)、在事件总线通知任务结束时之后:AEvent.SetEvent;//:事件总线通知:任务结束! TMonitor.Exit(self);
2、应当防止请求事件的组件(比如按钮)短时间内重复点击:所谓短时间内,指该请求事件被通知执行完毕的时间内(AEvent.SetEvent; //:事件总线通知:任务结束)。(1)、应当在任务开始前,禁用“请求事件的组件”:按钮等.Enabled:=true; (2)、在事件总线通知任务结束之后:AEvent.SetEvent;//:事件总线通知:任务结束后:按钮等.Enabled:=true;
procedure TfmxMatter.事件按钮等OnContitionsClick( Sender: TObject);
getDatabaseFromServer('ctl03001',300,1,50,
'select com_id,item_id,item_name,item_type, '
+' convert(Datetime,convert(varchar(23),maintenance_datetime,121)) as maintenance_datetime '
+' from ctl03001','物品资料');
end;
procedure TfmxMatter.getDatabaseFromServer(
pSql,pSelectKey:string);
var ATask:ITask; AEvent:TEvent;
ADataSet: TDataSet; //ifFinishTask:Integer;
begin
事件按钮等.Enabled:=false;
Memo1.Lines.Clear;
AEvent:=TEvent.Create; //:创建事件总线对象
//多任务处理预留: if arrayTasks[0].Status=TTaskStatus.Running then arrayTasks[0].Wait(INFINITE);
//ifFinishTask:=0;
AEvent:=TEvent.Create; //:创建事件总线对象
//创建并启动(多线程池化的)任务多核多处理器并行(多路CPU或GPU同时工作,或单处理器多核同步) //APool:= TThreadPool.Create; //:任务POSIX不能带池化参数产生
ATask:=TTask.Create(
procedure
var AtestFieldDefsCircle:Integer;
begin
TMonitor.Enter(self,0); //多核多处理器环境,经常用uses system;
try
try
if not Assigned(ADataSet) then //多核多处理器环境,单核环境或win32应用不存在此问题!
ADataSet:=
ClientModule1.ServerMethods1Client.getTFDDataSet(pSql,pSelectKey);
//:调用服务端方法: //:其中ADataSet处理方式很重要,
//:否则会报invalid Pionter错误:if not Assigned(ADataSet) then
while ADataSet.State=dsInactive do sleep(0);
if ADataSet.Active=true then ADataSet.First;
FDMemTMain_Temp.FieldDefs.Clear;
FDMemTMain_Temp.FieldDefs:=ADataSet.FieldDefs;
//Memo1.Lines.Add('TFDMemTable字段数:'+IntToStr(FDMemTMain_Temp.FieldDefs.Count));
FDMemTMain_Temp.Open;
while not (ADataSet.Eof) do
begin
FDMemTMain_Temp.Append;
for AtestFieldDefsCircle:=0 to FDMemTMain_Temp.FieldDefs.Count-1 do
begin
FDMemTMain_Temp.FieldByName(
FDMemTMain_Temp.FieldDefs[AtestFieldDefsCircle].Name).Value
:=ADataSet.FieldByName(
FDMemTMain_Temp.FieldDefs[AtestFieldDefsCircle].Name).Value;
end;
FDMemTMain_Temp.Post;
ADataSet.Next;
end;
//Memo1.Lines.Add('TFDMemTable记录数:'+IntToStr(FDMemTMain_Temp.RecordCount));
FDMemTMain_Temp.Last; FDMemTMain_Temp.First;//不像其基类TDataSet,TClientDataSet可以任意操纵Cursor,而且是双向的游标。
FDMemTMain_Temp.IndexesActive:=true;
while not (FDMemTMain_Temp.Eof) do
begin // 此段代码只用于调试,POSIX不允许重复操作Doublication not Allowed :
Memo1.Lines.Add(
'索引字段数:'+IntToStr(FDMemTMain_Temp.IndexFieldCount)+','
+'内存表索引记录数:'+IntToStr(FDMemTMain_Temp.IndexDefs.DataSet.RecordCount)+','
+'内存表索引:'+IntToStr(FDMemTMain_Temp.IndexDefs.DataSet.RecNo-1)+','
+'运营商编码:'+FDMemTMain_Temp.FieldByName('com_id').AsString.Trim+','
+'物品编码:'+FDMemTMain_Temp.FieldByName('item_id').AsString.Trim+','
+'物品名称:'+FDMemTMain_Temp.FieldByName('item_name').AsString.Trim+','
+'型号:'+FDMemTMain_Temp.FieldByName('item_type').AsString.Trim+','
);
FDMemTMain_Temp.Next;
end;
//:上面是任务的具体内容!
AEvent.SetEvent;//:事件总线通知:任务结束!
except // 万一异常,代码很重要 :
TMonitor.Exit(self);
事件按钮等.Enabled:=true;
AEvent.Free; //:出现异常释放:事件总线!
exit; //:出现异常就不要再往下执行了!
raise Exception.Create('出错或取消啦');
end;
finally
TMonitor.Exit(self);
事件按钮等.Enabled:=true;
//AEvent.Free;
//:切忌:将事件总线在try内释放AEvent.Free;
end;
//事件总线AEvent.SetEvent后:确认任务是否完成:
//Assert(AEvent.WaitFor(15000)>TWaitResult.wrSignaled);
//:调试时:确认任务结束时长的信息标记,即AEvent.SetEvent,然后:
AEvent.Free; //:确认收到任务结束的信号标记即AEvent.SetEvent后再:释放事件总线对象
end );
//多任务管理://arrayTasks[0]:=ATask;//TTask.WaitForAll(arrayTasks); //if arrayTasks[0].Status=TTaskStatus.Running then arrayTasks[0].Wait(INFINITE);
ATask.Start;
end;
// 商业及企业级应用:需要代码,请联系: qq : 584798030