代码之家  ›  专栏  ›  技术社区  ›  Holgerwa

如何正确访问后台线程中创建的查询结果?

  •  7
  • Holgerwa  · 技术社区  · 14 年前

    我想在后台线程中执行数据库查询。Omnithread库将帮助我处理所有线程方面的问题,但有一件事我目前还不明白:

    每个线程都需要单独的数据库连接。因此,后台线程创建DB连接,创建查询,然后执行它。

    现在,我可以使用后台线程的查询对象访问查询结果。
    但在执行查询之后,我希望访问 主要的 线程。

    如果我只是引用后台线程查询对象,这是否会导致问题,因为我正在访问另一个线程中的数据库连接?

    据我所知,在这种情况下,主线程不会有单独的DB连接,而是使用后台线程中不好的连接。

    我的思想在哪里被扭曲了?正确的方法是什么?

    2 回复  |  直到 14 年前
        1
  •  12
  •   mghie    14 年前

    如果您的OTL任务需要加载符合条件的公司的排序列表:

    // create and open query to fetch list of companies
    while not qryCompanies.Eof do begin
      C := TCompany.Create;
      try
        C.LoadFromDataset(qryCompanies);
        Companies.Add(C);
      except
        C.Free;
        raise;
      end;
      qryCompanies.Next;
    end;
    

    C 是公司的业务对象。它可以通过一个物体( TCompany )或接口( ICompany )由对象实现。 Companies 是一个 TList<TCompany> TList<ICompany> . 在任务结束时,您将公司列表发送到VCL线程:

    Task.Comm.Send(TOmniMessage.Create(MSGID_LIST_OF_COMPANIES, Companies));
    

    在窗体中,要在其中显示处理的公司列表 OnTaskMessage 事件 otlEventMonitor 监视任务的实例:

    procedure TListBaseFrame.otlEventMonitorTaskMessage(
      const task: IOmniTaskControl);
    var
      MsgID: word;
      MsgValue: TOmniValue;
    begin
      task.Comm.Receive(MsgID, MsgValue);
      Assert(MsgValue.IsInterface);
      if fLoaderTask = task then begin
        SetLoadedData(MsgID, MsgValue.AsInterface); // or MsgValue.AsObject);
        fLoaderTask := nil;
      end;
    end;
    

    公司列表将替换以前的列表,并可以显示在网格中。

    同样,您可以返回要显示和编辑的单个公司对象/界面。

    有两件事值得考虑:

    • 如果到目前为止您有接口的首选对象,那么编写多线程程序可能是重新考虑这一点的原因。如果您在后台线程中创建对象,然后将它们传递到VCL线程,并在后台线程中忘记它们,那么对象可能工作得很好。不过,我发现,通过在应用程序中缓存对象,并且仅从尚未加载或已更改的数据库中加载记录,可以获得更好的性能。我的所有表都附加了一个更改索引(64位整数,也可以使用时间戳),每次更新都会对其进行更改。而不是执行

      select * from foo where (...) order by (...)
      

      我只执行过

      select id, change_index from foo where (...) order by (...)
      

      然后在缓存中检查是否已存在具有相同ID(主键)和更改索引的对象,如果已存在,则返回缓存的对象,只有在不存在时才创建新的业务对象并加载所有列。

      但是,如果您缓存对象,那么您将从多个线程中获得对它们的引用,并且所有权问题很快变得非常复杂,因此基于引用计数的生存期管理是保持清醒的唯一方法。在这方面,使用接口而不是对象有很大帮助。

    • 如果多个线程可以同时访问每个业务对象,则需要向它们添加同步对象。这当然是可能的,但可能会带来额外的复杂性和潜在的死锁。如果将业务对象实现为不可变的,那么根本不需要锁。我越来越多地使用这种方法,虽然需要一些适应,但它可以大大简化事情。

        2
  •  5
  •   gabr    14 年前

    最好的方法可能是不要在GUI中使用数据库感知组件。线程应该与数据库通信,并将信息存储在业务对象中,然后将其发送到主线程(将显示它们)。

    多线程是很困难的,不仅从实现的角度,而且从应用程序设计的角度。通常,最好将后台线程视为具有定义良好的输入和输出的单独层。