- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
这是一个类似于 this one 的问题但背景不同。
编译器:Delphi 2010,即将推出Delphi XE5。
我已经构建了一个很好的应用程序,它通过 ZEOS components 管理远程 MySQL 服务器上的数据。 .
由于连接可能会失败并且 SQL 很慢,我使用了整洁的 OmniThreadLibrary创建一个 SQL 服务器看门狗并卸载大量加载到线程的“只读”表。
截至目前,我在主窗体显示之前手动创建了三个数据模块,每个模块都有独立的 TZConnection 和链接到同一数据模块 TZConnection 的一些 TZReadOnlyQuery 组件。每个线程从自身内部实例化其相关数据模块,然后执行查询。
看门狗工作得很好,但我对第二部分有一些疑问,即“只读”表线程。查询已经工作,但我还没有在主应用程序业务代码中使用它们的结果,我必须在其他表上插入和更新数据。
在我的计划中,我在主应用程序甚至连接到它们之前读取和加载所有这些“只读”数据集(整个线程间状态机已经完成)。理论上应该没有并发问题,因为“只读”表线程已完成其任务并且现在处于空闲状态。
但是我不知道如果此时我将控件或另一个数据集/数据源/从主窗体连接到空闲线程数据模块会发生什么。
我会不会因为主窗体 TZSession 与线程数据模块不一样而搞砸?我是否会在提交应用程序后才会发现罕见且令人讨厌的访问冲突(当然!)。基本上,我应该以什么样的信心或预防措施访问在另一个线程中创建的查询组件,假设只有主应用程序这样做并且仅用于读取数据?它甚至可能/健康吗?还是我错过了一些“最佳实践”的做法?
提前致谢。
最佳答案
我将发布我是如何做到的。由于时间不够,这将是“简洁”的(它仍然是一个巨大的文字墙!),如果您发现某些东西太晦涩,请随时提问。我不会假装自己写的既不是最好的,也不是最快的,也不是格式最好的代码。只需将其用作起点即可。
问题域的简短回顾:有一个软件可以在启动时使用线程“预打开”表。这让软件保持响应,甚至执行其他与数据库无关的启动任务。
我已经对这个程序进行了 1 个月的测试,所使用的数据库组件足以证明它不仅仅是一个“玩具演示”,在您添加第三个数据集时会崩溃。
原料:
如上所述:Delphi 2010+(我现在在 RAD Studio XE5 Ultimate 中运行它)但可能适用于早期版本。我只是没有测试它们。
cdsProduct => dspProduct => qryProduct
procedure TModDBPreload.ConnectToDatabase;
begin
dbcEShop.Connect;
SendStatusMessage('Loading languages archive');
qryLanguage.Open;
qryLanguage.First;
SearchOptions := [loCaseInsensitive];
ModApplicationCommon.ApplicationLocaleInfo.Lock;
...
try
...
// All the queries parameters needing a language id need to be assigned to the locked LocaleInfo object
qryGeoZone.Params.ParamByName('language_id').AsInteger := ModApplicationCommon.ApplicationLocaleInfo.LocaleIDForQueries;
cdsGeoZones.Params.ParamByName('language_id').AsInteger := ModApplicationCommon.ApplicationLocaleInfo.LocaleIDForQueries;
...
finally
ModApplicationCommon.ApplicationLocaleInfo.Unlock;
end;
SendStatusMessage('Loading countries archive');
cdsGeoZones.Open;
cdsGeoZones.First;
SendStatusMessage('Loading currencies archive');
qryCurrency.Open;
qryCurrency.First;
Sleep(100);
SendStatusMessage('Loading products archive');
cdsProduct.Open;
cdsProduct.First;
...
end;
SendStatusMessage('Loading languages archive');
qryLanguage.Open;
qryLanguage.First;
...
cdsGeoZones.Open;
cdsGeoZones.First;
Sleep(100)
是否可以让用户和开发人员在打开小表时看到消息。当然,一旦软件最终确定,可能会被删除。
Lock, try / finally
条款等是为了提醒您我们处于一个线程中,并且最好通过一些预防措施来访问某些资源。在这种特定情况下,我们有其他线程(与本文无关,因此未涵盖)因此我们必须保护一些数据结构。
type
TDBPreloadWorker = class(TOmniWorker)
protected
ThreadModDatabase : TModDBPreload;
FStatusString : string;
public
constructor Create;
function Initialize : boolean; override;
procedure Cleanup; override;
procedure SendStatusMessage(anID : Word; aValue : string = ''); overload;
procedure SendStatusMessage(aValue : string); overload;
procedure DisconnectFromDatabase;
procedure OMSendMessage(var msg: TOmniMessage); message MSG_SEND_MESSAGE;
procedure OMDisconnectFromDatabase(var msg: TOmniMessage); message MSG_DISCONNECT_FROM_DATABASE;
procedure OMUpdateStateMachine(var msg: TOmniMessage); message MSG_UPDATE_STATE_MACHINE;
end;
...
constructor TDBPreloadWorker.Create;
begin
Inherited;
FStatusString := 'Connecting to server...';
ThreadModDatabase := Nil;
end;
function TDBPreloadWorker.Initialize : boolean;
begin
ThreadModDatabase := TModDBPreload.Create(Nil);
ModDBPreload := ThreadModDatabase;
ThreadModDatabase.DBPreloadWorker := Self;
DisconnectFromDatabase; // In case of leftover Active := true from designing the software
Result := true;
end;
procedure TDBPreloadWorker.Cleanup;
begin
DisconnectFromDatabase;
ThreadModDatabase.Free;
ThreadModDatabase := Nil;
end;
procedure TDBPreloadWorker.SendStatusMessage(anID : Word; aValue : string);
begin
FStatusString := aValue; // Stored in case the main application polls a status update
Task.Comm.Send(anID, aValue);
end;
procedure TDBPreloadWorker.SendStatusMessage(aValue : string);
begin
SendStatusMessage(MSG_GENERAL_RESPONSE, aValue);
end;
procedure TDBPreloadWorker.DisconnectFromDatabase;
begin
if Assigned(ThreadModDatabase) then
ThreadModDatabase.DisconnectFromDatabase;
end;
procedure TDBPreloadWorker.OMSendMessage(var msg: TOmniMessage);
begin
Task.Comm.Send(MSG_GENERAL_RESPONSE, FStatusString);
end;
procedure TDBPreloadWorker.OMDisconnectFromDatabase(var msg: TOmniMessage);
begin
...
DisconnectFromDatabase;
end;
procedure TDBPreloadWorker.OMSendMessage(var msg: TOmniMessage);
begin
Task.Comm.Send(MSG_GENERAL_RESPONSE, FStatusString);
end;
procedure TDBPreloadWorker.OMUpdateStateMachine(var msg: TOmniMessage);
begin
Task.Comm.Send(MSG_GENERAL_RESPONSE, FStatusString); // Needed to show the pre-loaded status
if Assigned(ThreadModDatabase) then
begin
try
ThreadModDatabase.ConnectToDatabase;
SendStatusMessage('Reading database tables...');
if not ThreadModDatabase.QueryExecute then
begin
raise Exception.Create('Consistency check: the database does not return the expected values');
end;
SendStatusMessage(MSG_SUCCESS, 'Tables have been succesfully read');
SendStatusMessage(MSG_TASK_COMPLETED);
except
On E : Exception do
begin
DisconnectFromDatabase;
SendStatusMessage(MSG_TASK_FAILURE, E.Message);
end;
end;
end;
end;
function TDBPreloadWorker.Initialize : boolean;
procedure TDBPreloadWorker.SendStatusMessage(anID : Word; aValue : string);
procedure TDBPreloadWorker.OMUpdateStateMachine(var msg: TOmniMessage);
const
MSG_GENERAL_RESPONSE = 0;
MSG_SEND_MESSAGE = 1;
MSG_SHUTDOWN = 2;
MSG_SUCCESS = $20;
MSG_ABORT = $30;
MSG_RETRY = $31;
MSG_TASK_COMPLETED = $40;
MSG_FAILURE = $8020;
MSG_ABORTED = $8030;
MSG_TASK_FAILURE = $8040;
MSG_UPDATE_STATE_MACHINE = 9;
MSG_TIMER_1 = 10;
MSG_DISCONNECT_FROM_DATABASE = 99;
OnTaskMessage
事件指向:
procedure TFrmMain.monDBPreloadTaskMessage(const task: IOmniTaskControl;
const msg: TOmniMessage);
var
MessageString : string;
ComponentsNewState : boolean;
begin
MessageString := msg.MsgData.AsString;
if Length(MessageString) > 0 then
UpdateStatusBar(MessageString);
if task = FDBPreloadWorkerControl then
begin
if (msg.MsgID = MSG_TASK_COMPLETED) or (msg.MsgID = MSG_TASK_FAILURE) then
begin
ComponentsNewState := (msg.MsgID = MSG_TASK_COMPLETED);
// Unlike for the watchdog, the preload thread is not terminated
// The data is needed by the program till its end
// DBPreloadTerminate;
// Lets the main database queries be started
DBPreloadSuccess := (msg.MsgID = MSG_TASK_COMPLETED);
MainViewEnabled := ComponentsNewState;
if msg.MsgID = MSG_TASK_FAILURE then
begin
if MessageDlg('Unable to load the data tables from the database server', mtError, [mbRetry, mbAbort], 0) = mrAbort then
Close
else
// Reinitialize the preload thread.
...
end;
end;
end;
end;
procedure TFrmMain.UpdateStatusBar(Value : string);
begin
pnlStatusBar.SimpleText := Value;
pnlStatusBar.Update;
Application.ProcessMessages;
end;
procedure TModDatabase.ConnectToDatabase;
procedure ConnectDataSet(CDS : TClientDataSet; PreloadDataSet : TClientDataSet; RuntimeDataSet : TZAbstractRODataset; SetLanguage : boolean = false);
begin
// Only required by datasets needing a locale_id parameter
if (SetLanguage) then
begin
CDS.Params.ParamByName('language_id').AsInteger := ModApplicationCommon.ApplicationLocaleInfo.LocaleIDForQueries;
RuntimeDataSet.ParamByName('language_id').AsInteger := ModApplicationCommon.ApplicationLocaleInfo.LocaleIDForQueries;
end;
CDS.Data := PreloadDataSet.Data;
CDS.Active := true;
end;
begin
DisconnectFromDatabase;
dbcEShop.Connect;
UpdateStatusBar('Setting up products archive');
ConnectDataSet(cdsProduct, ModDBPreload.cdsProduct, qryProduct, true);
UpdateStatusBar('Setting up products options archive');
ConnectDataSet(cdsProductOption, ModDBPreload.cdsProductOption, qryProductOption);
UpdateStatusBar('Setting up options archive');
ConnectDataSet(cdsOption, ModDBPreload.cdsOption, qryOption);
UpdateStatusBar('Setting up options descriptions archive');
ConnectDataSet(cdsOptionDescription, ModDBPreload.cdsOptionDescription, qryOptionDescription, true);
...
关于mysql - 附加到先前由另一个线程创建的数据集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20814529/
我关注了 tutorial on creating a popup for an add-on在 Firefox 中,效果很好。 我现在遇到的问题是它创建的弹出窗口不会更改大小以适应我添加到其中的内容
我有一些视频,我需要连接一个标题并添加一些覆盖,问题是我需要先做覆盖,否则时间不正确,然后才将标题连接到视频的开头 ffmpeg -i talk.mp4 -i start_pancarte.png
我正在尝试附加一个 CSV 文件。这是我正在使用的线路。不幸的是,我找不到 export-csv 的附加选项。任何想法都有助于使其发挥作用。 Get-ADGroupMember "Domain Adm
我正在努力理解 Attach API (com.sun.tools.attach.*) 的用途。它的典型用途是什么?它是为了“模拟”JVM,以便您可以在不部署/启动代码的情况下测试您的代码吗?它是一个
我不明白为什么这不起作用。 soup_main = BeautifulSoup('FooBar') soup_append = BeautifulSoup('Meh') soup_main.body.
我有以下代码来返回我想要的字符串 $sql = " SELECT `description` FROM `auctions` WHERE `description` REGEX
我正在尝试从数组中附加具有多个值的元素,但我做错了。这是我的代码: for(var i=0; i ` + pricesArray[i].start_date ` ` + pricesArray[i
我正在尝试将图像链接添加到此 javascript 附加表中。使图像位于按钮上方 这是代码 $("#1").append(""+section+""+no+""+price+""+button+""
我有一个问题,我已经解决了,但它太烦人了。 我有一个 js 代码,当使用“追加”按下按钮时,它会放下一些 html 代码,并且通过该代码,我为 x 按钮提供了一个 id,并为容器元素提供了一个 id。
我想逐行读取文件,并且每一行可能都有很多字符。 这个版本的readline效果很好 func readLine(r *bufio.Reader) ([]byte, error) { var (
我有一个网站,每次用户登录或注销时,我都会将其保存到文本文件中。 如果不存在,我的代码在附加数据或创建文本文件时不起作用。这是示例代码 $myfile = fopen("logs.txt", "wr"
我正在尝试使用 typescript 和 Formik 创建一个自定义输入字段。我可以就完成以下代码的最佳方式获得一些帮助吗?我需要添加额外的 Prop 标签和名称......我已经坚持了一段时间,希
我有一个字符串 big_html,我想将它添加到某个 div 中。我观察到以下方面的性能差异: $('#some-div').append( big_html ); // takes about 10
如何使用 FormData 创建以下结果 ------WebKitFormBoundaryOmz20xyMCkE27rN7 Content-Disposition: form-data; name="
有没有办法附加 jQuery 事件处理程序,以便在任何先前附加的事件处理程序之前触发该处理程序?我遇到了this article ,但代码不起作用,因为事件处理程序不再存储在数组中,而这正是他的代码所
我正在开发一个需要网络登录的 iPhone 应用程序。像往常一样我打电话 [[UIApplication sharedApplication] openURL:loginURL]; 这将关闭应用程序并
我想开发一个仅针对特定域激活的扩展。 我不希望它在不浏览此特定域时出现在浏览器菜单中。 有可能这样做吗? 最佳答案 可能:对于菜单,您可以添加一个弹出窗口侦听器,用于检查当前加载的URL(docs f
这段 JavaScript 代码 function writeCookie(CookieName, CookieValue, CookieDuration) { var expiration
我正在使用 Handlebars 来渲染使用ajax从本地服务器获得的信息。我的 HTML 看起来像: {{#each Tabs}}
我尝试了以下代码,但当输入框中没有数据时它不会通知。当我直接添加此内容(不附加)时,它会起作用。我在这里做错了什么 var output = "\n"+ "\n"+
我是一名优秀的程序员,十分优秀!