关键部分会帮助解决这个问题吗?

时间:2012-10-11 04:01:17

标签: delphi thread-safety critical-section indy10

使用Indy 10 TCP命令处理程序,每次我收到命令时,我都会在数据库中插入一行,然后从数据库中读取整个故事来更新字符串网格。

我正在使用AnyDac数据库组件,他们的文档说“连接对象和所有与它关联的对象(如TADQuery,TADTransaction等)在每个时刻都必须由单个线程使用”。

如果我“慢慢”发送TCP命令,则没有问题。如果我“快速”发送它们(当AnyDac仍然显示SQL游标时,我得到一个异常的EDatabaseError“字段未找到” - 但它当然存在。

当我收到TCP命令时,我的代码使用PostMEssage将UM_发送到我的主表单。

认为正在发生的事情是,主要表单是从第一个TCP命令读取表中的所有行,而TCP命令处理程序是插入新行的一半作为第二个命令的结果 - 当我调用ADQuery1.FieldByName()时,“找不到字段”。

这听起来像是问题吗?

如果是这样,我该如何预防呢?可以使用Critical Section(其中,主线程?)?或者还有其他方式吗?


[更新]我刚刚意识到 - 这不是一个线程问题(我认为)。当我收到TCP命令时,我使用PostMessage()将UM_发送到我的主窗体。因此,无论TCP命令的速度有多快,我的主表单只能通过其消息队列处理一个UM_。 TCP命令处理程序只是发送该消息的一行 - 无d / b访问。

但是 - 我不明白的是,如果在TCP命令之间留出“一段时间”一切都很好,但如果我“快速”发送它们,那么我得到的例外是该行中没有这样的字段桌子。


[更新] 事实上,我终于解决了它,问题是使用相当慢的更新TStringGrid。 Tehre是让它更快的方法,但我决定将它转换为TDbGrid,它可以非常迅速地更新。

2 个答案:

答案 0 :(得分:3)

听起来您正在从后台线程中运行的TCP命令处理程序更新AnyDac数据库,并从主线程读取AnyDac数据库。由于AnyDac表示它们不支持多线程访问,因此会产生问题。

一种选择是尝试使所有参与的线程排队并等待访问AnyDac数据库。这违背了使用多个线程开始的目标 - 为什么要使用多个线程,如果你要做的就是让它们排队并按顺序执行?添加互斥锁(如关键部分)会增加创建死锁情况的可能性,如果您计划在应用的重绘逻辑中获取该锁,则尤其是。你绝对不想在重绘逻辑中使用锁。

另一种方法是将数据库更新移动到主线程中。执行此操作仍然需要一些线程同步,但是onus是在后台线程上,而不是在主线程上。

将数据库更新移动到主线程的一种相对简单的方法是让TCP命令处理程序将自定义UM_消息发布到主窗口(正如您已经在做的那样)附加数据消息。消息处理程序从消息中选取数据,使用数据更新数据库,并处理附加到消息的数据。

使用这种方法,所有对AnyDac数据库对象的访问都发生在主线程中,因此没有多线程问题。除非有人在重绘周期中调用Application.ProcessMessages,否则这应该解决重绘过程中发生数据库更新的任何问题。

答案 1 :(得分:2)

如果通过调用PostMessage()传递数据,则必须确保在处理主线程中的消息期间数据仍然有效。最简单的方法是在处理程序中执行以下操作:

  • 创建新对象
  • 将必要的数据复制到新创建的对象中
  • 调用PostMessage以新对象作为参数发送消息
 

    procedure ...
    var MsgData: TMsgData;
    begin
      MsgData := TMsgData.Create(ACommand);
      PostMessage(MainFormHWND, WM_MYMSG, Integer(MsgData), 0);  
    end

 

在消息的处理程序中

  • 处理消息
  • 释放对象
 

    procedure MainForm.HandleMsg(var M: TMessage);
    var MsgData: TMsgData;
    begin
      MsgData := TMsgData(M.WParam);
      // Process command
      MsgData.Free();  
    end;