【问题标题】:How to properly access query-results created in background thread?如何正确访问在后台线程中创建的查询结果?
【发布时间】:2010-02-21 10:19:08
【问题描述】:

我想在后台线程中执行数据库查询。 OmniThread 库将帮助我处理所有线程的内容,但目前我不明白一件事:

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

现在我可以使用后台线程的查询对象访问查询结果。
但是查询执行完毕后,我想在ma​​in线程中访问查询结果。

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

据我了解,在这种情况下,主线程不会有单独的数据库连接,而是使用来自后台线程的那个不好。

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

【问题讨论】:

    标签: database multithreading delphi delphi-2009


    【解决方案1】:

    如果您的 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)或对象实现的接口(@98​​7654324@)实现。 CompaniesTList<TCompany>TList<ICompany>。在任务结束时,您将公司列表发送到 VCL 线程:

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

    在您想要显示您处理正在监视您的任务的otlEventMonitor 实例的OnTaskMessage 事件的公司列表的表单中:

    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】:

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

    多线程很难,不仅从实现的角度来看,从应用程序设计的角度来看也是如此。通常最好将后台线程视为具有明确定义的输入和输出的单独层。

    【讨论】:

    • "..并将信息存储在业务对象中。"这就是我卡住的地方。我不使用任何数据感知组件,我只需要将查询结果数据传递到主线程。您将什么称为业务对象?
    • 业务对象 = 包含数据库数据的类的实例,转换为面向对象的编程方式(即看起来不像 SQL 结果,而是像您学习时在纸上设计的东西你的问题)。
    猜你喜欢
    • 2016-08-21
    • 2014-04-02
    • 2019-12-22
    • 2016-09-12
    • 2023-03-14
    • 1970-01-01
    • 2017-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多