我想在后台线程中执行数据库查询。 OmniThread 库将帮助我处理所有线程问题,但到目前为止我不明白一件事:
每个线程都需要一个单独的数据库连接。因此,后台线程创建数据库连接,创建查询,然后执行它。
现在我可以使用后台线程的查询对象访问查询结果。
但是执行查询后,我想访问查询结果main thread.
如果我只引用后台线程查询对象,这是否会导致问题,因为我正在另一个线程中访问数据库连接?
据我了解,在这种情况下,主线程不会有单独的数据库连接,而是使用后台线程中的连接,这是不好的。
我的想法在哪里扭曲了,正确的方法是什么?
如果您的 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
is a TList<TCompany>
or 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(主键)和更改索引的对象,如果存在则返回缓存的对象,如果不存在则创建新的业务对象并加载所有列。
但是,如果您缓存对象,您将从多个线程获得对它们的引用,并且所有权问题很快就会变得如此复杂,以至于基于引用计数的生命周期管理是保持理智的唯一方法。使用接口而不是对象在这方面有很大帮助。
如果多个线程可以同时访问每个业务对象,则有必要为每个业务对象添加同步对象。这当然是可能的,但可能会带来额外的复杂性和潜在的死锁。如果您将业务对象实现为不可变的,则根本不需要锁。我越来越多地使用这种方法,虽然需要一些时间来适应它,但它可以使事情简化很多。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)