2

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

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

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

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

认为正在发生的事情是,由于第一个 TCP 命令,主要形式是从表中读取所有行,而 TCP 命令处理程序在中途插入一个新行作为第二个命令的结果 - 因此“当我调用ADQuery1.FieldByName().

这听起来像问题吗?

如果是这样,我该如何预防?可以使用关键部分(在哪里,主线程?)?还是有其他方法?


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

但是 - 我不明白的是,如果在 TCP 命令之间留出“一段时间”一切都很好,但如果我“快速”发送它们,那么我会收到异常说在表的那一行中没有这样的字段。


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

4

2 回答 2

3

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

一种选择是尝试让所有参与的线程排队等待访问 AnyDac 数据库。这违背了使用多个线程开始的目标 - 如果您要做的只是让它们排列并按顺序执行,为什么还要使用多个线程?添加互斥锁(如临界区)会增加您创建死锁情况的机会,特别是如果您计划在应用程序的重绘逻辑中获取该锁。您绝对不希望在重绘逻辑中锁定。

另一种方法是将数据库更新移动到主线程中。执行此操作仍然涉及一些线程同步,但责任在于后台线程,而不是您的主线程。

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

使用这种方法,对 AnyDac 数据库对象的所有访问都发生在主线程中,因此不存在多线程问题。除非有人在做一些愚蠢的事情,比如在重绘周期中间调用 Application.ProcessMessages,否则这应该可以解决重绘期间发生的数据库更新的任何问题。

于 2012-10-11T04:52:58.910 回答
2

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

  • 创建一个新对象
  • 将必要的数据复制到新创建的对象中
  • 调用 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;

 
于 2012-10-11T11:41:19.657 回答