長いクエリを別のスレッドで実行して、それらを中止し、ユーザーにフィードバックを提供したいと考えています。これはすべて機能していますが、OnNotice イベントの処理が正しく行われていないため、アクセス違反が発生することがあります。これを行う適切な方法を知りたいのです。
Delphi 2010 で Devart の PgDAC と OmniThreadLibrary を使用しています。
私が実行する PostgreSQL コードは、次のようなものを含むストアド プロシージャです。
RAISE NOTICE 'ad: %',myad.name;
ここに私のコードの興味深い部分があります:
procedure TFDecomptes.FormCreate(Sender: TObject);
begin
ThreadConnection := TPgConnection.Create(Self);
ThreadConnection.Assign(DM.PgConnection1);
end;
これThreadConnection
は、(別のスレッド内で) クエリを実行するために使用される TPgConnection です。
procedure TFDecomptes.BInterruptClick(Sender: TObject);
begin
ThreadConnection.BreakExec;
end;
これが「クエリの中断」ボタンの機能です。メインスレッドで使用されているため、これが非常に「スレッドセーフ」であるかどうかはわかりませんが、クエリ実行スレッド専用の TPgConnection で何かを行います。
procedure TFDecomptes.OmniEventMonitor1TaskMessage(const task: IOmniTaskControl; const msg: TOmniMessage);
begin
case msg.MsgID of
1: begin
CalculationError:=msg.MsgData.AsString;
end;
end;
end;
これは、スレッドの実行中に発生したエラー (SQL エラーやクエリのキャンセルなど) を表示する場所です。
procedure TFDecomptes.PgConnectionNotice(Sender: TObject; Errors: TPgErrors);
var s:String;
begin
s:=Errors[Errors.Count-1].ToString;
if copy(s,1,4)='ad: ' then begin
delete(s,1,4);
LAD.Caption:=s;
end;
end;
これが OnNotice イベント処理です。ラベルのキャプションを変更するだけです。
procedure InternalExecQuery(const task: IOmniTask);
Var q:TPgSQL;
begin
q:=Task.Param['pgsql'];
Try
q.Execute;
Except
On E:Exception do task.Comm.Send(1,e.Message);
End;
end;
procedure TFDecomptes.StartClick(Sender: TObject);
begin
ThreadConnection.OnNotice:=PgConnectionNotice;
Timer1.Enabled:=True;
CalculationTask := CreateTask(InternalExecQuery, 'CalculeDecomptes')
.MonitorWith(OmniEventMonitor1)
.SetParameter('pgsql', PgSQL)
.Run;
end;
そして、これがクエリの実行方法です。
そのため、PgConnectionNotice
(メイン スレッドで実行されている) イベントは (クエリ実行スレッドで使用されている) に関連付けられておりThreadConnection
、これがランダム アクセス違反を生成していると思われます。
これを処理する方法がわかりません。lock
PgConnectionNotice 内で何らかの種類を使用する必要があります (同期しますか?)。
これは私が試したものです:
procedure TFDecomptes.OmniEventMonitor1TaskMessage(const task: IOmniTaskControl; const msg: TOmniMessage);
begin
case msg.MsgID of
1: begin
CalculationError:=msg.MsgData.AsString;
end;
2: begin
lad.caption:='Here';
end;
end;
end;
procedure TFDecomptes.PgConnectionNotice(Sender: TObject; Errors: TPgErrors);
begin
// I am not using the passed string in this test
CalculationTask.Comm.Send(2,Errors[Errors.Count-1].ToString);
end;
PgConnectionNotice (MsgId=2) で送信されたメッセージが で受信されませんOmniEventMonitor1TaskMessage
。
使用してみCalculationTask.Invoke
ましたが、文字列パラメーターを渡すために呼び出す方法がわかりませんでした (Delphi 2010 では無名関数が許可されていないと思います)。
このようにクエリをキャンセルするというより簡単なアクションを試したところ、クエリのキャンセルが停止しました:
procedure TFDecomptes.DoTheInterrupt;
begin
ThreadConnection.BreakExec;
end;
procedure TFDecomptes.BInterruptClick(Sender: TObject);
begin
CalculationTask.Invoke(DoTheInterrupt);
end;
だから私は経由で呼び出しを行うべきではないと思いますCalculationTask
。作成したタスクをInternalExecQuery
グローバル変数に保存して使用する必要がありますか?