异步TADOQuery的OnFetchComplete未同步到主线程
在[eoAsyncFetchNonBlocking]
使用TADOQuery
并附加到OnFetchComplete
事件时,我发现OnFetchComplete
未在主线程中执行(在XE4和XE8中测试过)。 我认为这是一个错误*,因为我们大多数人都会在这些类型的事件中在UI上工作。 我认为这是大型项目中一些问题的根源,我需要一个解决方法。
[编辑]在阅读ADO文档后,我知道这可能不是一个错误,但多线程问题依然存在。
有没有一种优雅的方式来强制在这个处理程序中的代码在主线程上执行? 我不想使用计时器(但如果这是我将采取的唯一解决方案)。 或者,是否有ADO Synchronization对象,我可以在此处等待或向ADO提供程序发送其他形式的信号?
这是一个简单的示例,显示了这个问题。 我的项目对于创建和填充这些数据集的工厂来说更加复杂,但在此将类似的数据集附加到ADOQuery1FetchComplete
的网格中。
unit Unit4;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Data.Win.ADODB, Vcl.StdCtrls;
type
TForm4 = class(TForm)
Button1: TButton;
Button2: TButton;
ADOQuery1: TADOQuery;
ADOConnection1: TADOConnection;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure ADOQuery1FetchComplete(DataSet: TCustomADODataSet;
const Error: Error; var EventStatus: TEventStatus);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FMainThreadID : DWORD;
public
{ Public declarations }
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
procedure TForm4.ADOQuery1FetchComplete(DataSet: TCustomADODataSet;
const Error: Error; var EventStatus: TEventStatus);
begin
Assert(FMainThreadID = GetCurrentThreadId); //this assertion fails!
// I need UI code here to run FMainThreadID
end;
procedure TForm4.Button1Click(Sender: TObject);
begin
ADOQuery1.Open;
end;
procedure TForm4.FormCreate(Sender: TObject);
begin
FMainThreadID := GetCurrentThreadId;
end;
end.
而dfm只是简单地设置了ExecuteOptions = [eoAsyncFetchNonBlocking]
和OnFetchComplete
处理的查询。
object Form4: TForm4
Left = 0
Top = 0
Caption = 'Form4'
ClientHeight = 186
ClientWidth = 258
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 24
Top = 88
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object ADOQuery1: TADOQuery
Connection = ADOConnection1
ExecuteOptions = [eoAsyncFetchNonBlocking]
OnFetchComplete = ADOQuery1FetchComplete
Parameters = <>
SQL.Strings = (
'SELECT * FROM TABLENAME')
Left = 144
Top = 16
end
object ADOConnection1: TADOConnection
Connected = True
ConnectionString =
'Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security In' +
'fo=False;Initial Catalog=DBNAME;Data Source=.INSTANCENAME'
LoginPrompt = False
Provider = 'SQLOLEDB.1'
Left = 40
Top = 16
end
end
[编辑]建议使用TThread.Sychronize
,但这不是一个Delphi线程。
如果GetCurrentThreadId
不足以证明处理程序是从另一个线程调用的,则这里是主线程和有问题的线程的调用堆栈(为了更好的度量,我在主线程中添加了一个sleep)
主线程正在睡觉
:77d0c7bc ntdll.ZwDelayExecution + 0xc
:7745104f KERNELBASE.Sleep + 0xf
Unit6.TForm6.btnQueryClick($32BC00)
Vcl.Controls.TControl.Click
Vcl.StdCtrls.TCustomButton.Click
Vcl.StdCtrls.TCustomButton.CNCommand(???)
Vcl.Controls.TControl.WndProc((48401, 1344, 7275840, 0, 1344, 0, (), 1344, 111, (), 0, 0, ()))
Vcl.Controls.TWinControl.WndProc((48401, 1344, 7275840, 0, 1344, 0, (), 1344, 111, (), 0, 0, ()))
Vcl.StdCtrls.TButtonControl.WndProc((48401, 1344, 7275840, 0, 1344, 0, (), 1344, 111, (), 0, 0, ()))
Vcl.Controls.TControl.Perform(???,???,7275840)
Vcl.Controls.DoControlMsg(???,(no value))
Vcl.Controls.TWinControl.WMCommand((273, (), 1344, 0, (), 7275840, 0))
Vcl.Forms.TCustomForm.WMCommand((273, (), 1344, 0, (), 7275840, 0))
Vcl.Controls.TControl.WndProc((273, 1344, 7275840, 0, 1344, 0, (), 1344, 111, (), 0, 0, ()))
Vcl.Controls.TWinControl.WndProc((273, 1344, 7275840, 0, 1344, 0, (), 1344, 111, (), 0, 0, ()))
Vcl.Forms.TCustomForm.WndProc((273, 1344, 7275840, 0, 1344, 0, (), 1344, 111, (), 0, 0, ()))
Vcl.Controls.TWinControl.MainWndProc(???)
System.Classes.StdWndProc(2829362,273,1344,7275840)
:759b8e71 user32.CallNextHookEx + 0xb1
:759b90d1 ; C:windowsSysWOW64user32.dll
:759b932c ; C:windowsSysWOW64user32.dll
:759b9529 ; C:windowsSysWOW64user32.dll
:77d107d6 ntdll.KiUserCallbackDispatcher + 0x36
:759be4a9 ; C:windowsSysWOW64user32.dll
:711f19e4 ; C:windowsWinSxSx86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.9600.17810_none_a9edf09f013934e0comctl32.dll
:711f1a7b ; C:windowsWinSxSx86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.9600.17810_none_a9edf09f013934e0comctl32.dll
:759b8e71 user32.CallNextHookEx + 0xb1
:759b90d1 ; C:windowsSysWOW64user32.dll
:759bddd5 user32.CallWindowProcW + 0x95
Vcl.Controls.TWinControl.DefaultHandler(???)
:00532947 TWinControl.DefaultHandler + $EB
:00532836 TWinControl.WndProc + $5CA
:00544cdd TButtonControl.WndProc + $71
:004c9162 StdWndProc + $16
:759b8e71 user32.CallNextHookEx + 0xb1
:759b90d1 ; C:windowsSysWOW64user32.dll
:759ba66f ; C:windowsSysWOW64user32.dll
:759ba6e0 user32.DispatchMessageW + 0x10
:005bb158 TApplication.ProcessMessage + $F8
:00040000
其他线程调用处理程序
Unit6.TForm6.QueryFetchComplete($288B3E0,nil,esOK)
Data.Win.ADODB.TCustomADODataSet.FetchComplete(nil,89849068,Pointer($3299D8) as _Recordset)
:6b7ab81d ; C:Program Files (x86)Common FilesSystemadomsado15.dll
:6b7ab4b6 ; C:Program Files (x86)Common FilesSystemadomsado15.dll
:6b7a17c8 ; C:Program Files (x86)Common FilesSystemadomsado15.dll
:6b7b616f ; C:Program Files (x86)Common FilesSystemadomsado15.dll
:69038991 ; C:Program Files (x86)Common FilesSystemmsadcmsadce.dll
:69038bd6 ; C:Program Files (x86)Common FilesSystemmsadcmsadce.dll
:69038d54 ; C:Program Files (x86)Common FilesSystemmsadcmsadce.dll
:69037a02 ; C:Program Files (x86)Common FilesSystemmsadcmsadce.dll
:69021205 ; C:Program Files (x86)Common FilesSystemmsadcmsadce.dll
:69038034 ; C:Program Files (x86)Common FilesSystemmsadcmsadce.dll
:77a07c04 KERNEL32.BaseThreadInitThunk + 0x24
:77d2ad1f ntdll.RtlInitializeExceptionChain + 0x8f
:77d2acea ntdll.RtlInitializeExceptionChain + 0x5a
根据我的经验,更简单的方法是使用:
同步或TThread.Queue
这不是一个错误,或者至少不是一个VCL错误。 这种行为是由提供者处理的,我们不能说它没有遵循规范,因为没有关于如何管理这些事件的异步的规范。 所有规格说明如下:
那么adAsyncFetchNonBlocking
表示检索时主线程永远不会阻塞。 如果所请求的行未被检索到,则当前行自动移动到文件末尾。
这是警告执行完成的主线程的代码示例:
procedure TForm1.ADOQuery1FetchComplete(DataSet: TCustomADODataSet;
const Error: Error; var EventStatus: TEventStatus);
begin
TThread.Synchronize(nil,
procedure
begin
ShowMessage('FetchData Completed');
end
);
end;
更新:
我证实了这一点。 它将适用于版本6,7,XE4和XE7(我在这里没有其他版本)。 使用Synchronize
注入代码以执行到主线程上下文没有任何问题。 另外,我想引起您的注意,DataSet仅仅是一个指向ADOQuery对象的指针(实际上是一个引用),所以您不一定必须在匿名方法中引用它,这对旧版本来说是一个重要的事实像6或7,因为匿名方法不存在。
奖金阅读:活动
链接地址: http://www.djcxy.com/p/27111.html上一篇: Asynchronous TADOQuery's OnFetchComplete not synchonized to main thread
下一篇: Distributed Transaction on Linked Server between sql server and mysql