TCustomClientDataSet CloneCursor线程是否安全?

时间:2015-08-27 23:49:56

标签: multithreading delphi tclientdataset

我已经读过克隆的TClientDataSets是线程安全的,如果克隆是只读的(没有发布记录或重新加载数据)

Delphi - Is TClientDataset Thread Safe?

但是我关心CloneCursor方法本身;该方法以调用源DataSet的SetNotifyCallback方法结束,如果FNotifyCallback为False,该方法将回调方法传递给它的IDSCursor:

procedure TCustomClientDataSet.SetNotifyCallback;
begin
  if not FNotifyCallback then
  begin
    Check(FDSCursor.SetNotifyCallBack(IntPtr(Self), @TCustomClientDataSet.NotifyCallback));
    FNotifyCallback := True;
  end;
end; 

在极少数情况下有两个DataSet,A& B,在几乎同时克隆到DataSet C的单独线程中(DataSet C的FNotifyCallback False),A略微超前B.在A检查FNotifyCallback之后但在A已将FNotifyCallBack设置为True之前,B开始执行C的SetNotifyCallBack以上。

在这种情况下,DataSet C的FDSCursor SetNotifyCallback方法几乎同时被两个不同的线程调用;一个在IDSCursor中写入变量引用的方法(我假设;没有运气找到源代码)。不可否认,两个调用都要求存储相同的引用,但正如标题所要求的那样,CloneCursor是否是线程安全的?

请提前接受我的谢意。

2 个答案:

答案 0 :(得分:0)

问题中提出的问题围绕着两个线程A和B同时克隆到C的不太可能的事件,这些代表了自C打开以来对C的CloneCursor方法的第一次调用(FNotifyCallback False)。这导致C的IDSCursor SetNotifyCallback可能同时被两个独立的线程调用。

根据Graymatter的评论,一个解决方案是确保第一次调用C的CloneCursor发生在C的线程中。完成此操作后,FNotifyCallback为True,并且只要C保持打开,它就会保持不变。

使用FNotifyCallback True,C的SetNotifyCallback代码解析为以下指令(在我的电脑上):

006418CB  cmp byte ptr [ebx+$00000290],$00
006418D2  jnz $006418fa
{code to call IDSCursor SetNotifycallback}
006418FA 5B               pop ebx
006418FB C3               ret 

使用FNotifyCallback True,在跳转到弹出和返回指令之前,SetNotifyCallback会将FNotifyCallback的内容与零值进行比较。

冒着遭到殴打的风险,我相信一旦FNotifyCallback为True,那么后续对SetNotifyCallback的调用是线程安全的。

虽然问题的主体侧重于SetNotifyCallback,但问题专门针对CloneCursor。唯一关注的其他领域(我可以看到)围绕将克隆DataSet的FDSBase分配给源(本讨论中的C)FDSBase。这将增加源FDSBase接口的引用计数。

在我的电脑上,这解析为一条增加内存位置的指令:

inc dword ptr [eax+$04] 

我假设是原子的。另外,我无法相信使接口的refcount线程的递增和递减安全,这不是首次提出接口时考虑的第一件事。

总之,一旦FNotifyCallback为True,我相信CloneCursor是线程安全的。

答案 1 :(得分:0)

一个很老的问题,但是我在任何地方都没有很好的回答,不得不自己摸一下。因此,在此处为仍然可以使用此类工具的其他人发布信息。

CloneCursor并不是线程安全的,至少在最近我测试过的Delphi 2007中如此。如果在多个线程中调用CloneCursor,每个线程都有自己的克隆数据集,而所有这些克隆都从同一源数据集进行克隆,则会得到AV或其他错误。重现这一点很容易:只需创建两个或多个线程,这些线程就可以在while循环中继续克隆相同的数据集,并且最多会在几秒钟内出现错误。但是,您可以只在关键部分保护对CloneCursor的调用,并且可以在关键部分之外使用克隆。

现在,您提出的另一种解决方法是,在两个或多个线程中将不同的源克隆到同一克隆的数据集中,这对我来说是没有意义的,我什至也不必费心测试是否会引发错误或不会,因为这样的用法在设计上似乎是错误的。如果您需要这种用法,建议您直接进入CriticalSection。

相关问题