如何正确访问在后台线程中创建的查询结果? [英] How to properly access query-results created in background thread?

查看:146
本文介绍了如何正确访问在后台线程中创建的查询结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在后台线程中执行数据库查询。 OmniThread库将帮助我所有的线程的东西,但有一件事我不明白到目前为止:



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



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



到后台线程查询对象,这是否会导致问题,因为我正在访问另一个线程中的DB连接?



根据我的理解,在这种情况下,主线程不会有它的独立的数据库连接,并使用一个从后台线程不好。

$

解决方案

如果我的想法扭曲了,您的OTL任务需要加载符合条件的公司的排序列表:

  //创建并打开查询以获取列表公司
而不是qryCompanies.Eof做开始
C:= TCompany.Create;
try
C.LoadFromDataset(qryCompanies);
Companies.Add(C);
except
C.Free;
raise;
end;
qryCompanies.Next;
end;

C 。它可以由对象实现的一个对象( TCompany )或一个接口( ICompany )。 公司 TList< 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); //或MsgValue.AsObject);
fLoaderTask:= nil;
end;
end;

公司列表替换上一个列表,可以显示在网格中。



同样,你可以返回一个公司对象/界面来显示和编辑。



有两件值得思考的事情: / p>


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

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

    我只执行一次

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

    然后检查缓存中是否存在具有相同id(主键)和变更索引的对象,如果是,则返回缓存对象,并且仅当未创建新业务对象并加载所有列。



    但是如果你缓存对象,你将有多个线程的引用,所有权问题很快就会变得如此复杂,基于引用计数的生命周期管理是唯一的方法。


  • 如果多个线程可以同时访问它们,则需要向每个业务对象添加同步对象。当然可能,但可能引入额外的复杂性和潜在的死锁。如果将业务对象实现为不可变,则不需要任何锁。我越来越多地使用这种方法,虽然它需要一些习惯,它可以简化很多东西。



I want to execute a database query in a background thread. The OmniThread library will help me with all the thread stuff, but there is one thing I don't understand so far:

Every thread needs a separate database connection. The background thread therefore creates the DB connection, creates the query and then executes it.

Now I could access the query results using the query object of the background thread.
But after the query is executed, I want to access the query results in the main thread.

If I just refer to the background-thread query object, does this cause problems because I am accessing a DB connection in another thread?

As I understand, in this case the main thread would not have it's separate DB connection and use the one from the background thread which is not good.

Where is my thinking distorted and what's the right way to do this?

解决方案

If your OTL task needs to load a sorted list of companies that match the criteria:

// 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 is your business object for a company. It may by an object (TCompany) or an interface (ICompany) implemented by the object. Companies is a TList<TCompany> or TList<ICompany>. At the end of the task you send the list of companies to the VCL thread:

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

In the form where you want to display the list of companies you handle the OnTaskMessage event of the otlEventMonitor instance that is monitoring your task:

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;

The list of companies replaces the previous list and can be displayed in the grid.

Similarly you could return a single company object / interface to be displayed and edited.

Two things that are worth thinking about:

  • If you have so far preferred objects to interfaces, writing multi-threaded programs may be a reason to reconsider that. If you create your objects in a background thread, then pass them to the VCL thread and forget about them in the background thread, then objects may work well. I found however that much better performance can be had by caching objects in the application, and loading only records from the database that haven't been loaded yet, or that have changed. All my tables have a change index (64 bit integer, a time stamp may work as well) attached to it, that is changed with every update. Instead of executing a

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

    I only ever execute a

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

    then check in the cache whether an object with the same id (primary key) and change index already exists, if so return the cached object, and only if not create a new business object and load all columns.

    But if you cache objects you will have references to them from multiple threads, and ownership issues soon get so complicated that lifetime management based on reference counts is the only way to stay sane. Using interfaces instead of objects helps a lot in this regard.

  • Adding a synchronization object to each business object is necessary, if multiple threads can access them concurrently. It is of course possible, but may introduce additional complexity and potential deadlocks. If you implement your business objects as immutable, then no locks are needed at all. I am using that approach more and more, and while it takes some getting used to it it can simplify things a lot.

这篇关于如何正确访问在后台线程中创建的查询结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆