使用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,它可以非常迅速地更新。
答案 0 :(得分:3)
听起来您正在从后台线程中运行的TCP命令处理程序更新AnyDac数据库,并从主线程读取AnyDac数据库。由于AnyDac表示它们不支持多线程访问,因此会产生问题。
一种选择是尝试使所有参与的线程排队并等待访问AnyDac数据库。这违背了使用多个线程开始的目标 - 为什么要使用多个线程,如果你要做的就是让它们排队并按顺序执行?添加互斥锁(如关键部分)会增加创建死锁情况的可能性,如果您计划在应用的重绘逻辑中获取该锁,则尤其是。你绝对不想在重绘逻辑中使用锁。
另一种方法是将数据库更新移动到主线程中。执行此操作仍然需要一些线程同步,但是onus是在后台线程上,而不是在主线程上。
将数据库更新移动到主线程的一种相对简单的方法是让TCP命令处理程序将自定义UM_消息发布到主窗口(正如您已经在做的那样)和附加数据消息。消息处理程序从消息中选取数据,使用数据更新数据库,并处理附加到消息的数据。
使用这种方法,所有对AnyDac数据库对象的访问都发生在主线程中,因此没有多线程问题。除非有人在重绘周期中调用Application.ProcessMessages,否则这应该解决重绘过程中发生数据库更新的任何问题。
答案 1 :(得分:2)
如果通过调用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;