長いクエリを別のスレッドで実行して、それらを中止したり、ユーザーにフィードバックを与えたりしたい。これはすべて動作していますが、OnNoticeイベントの処理が正しい方法で行われていないため、アクセス違反が発生することがあります。これを行う正しい方法を知りたいと思います。私はデルファイ2010年にPostgreSQLのイベントを、DevartのPgDACコンポーネントを使用してDelphiの別のスレッドに処理する
私は実行PostgreSQLのコードをDevartのPgDACとOmniThreadLibraryを使用しています
であるようなものが含まれ、ストアドプロシージャ:ここ
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;
これがクエリの実行方法です。
ThreadConnection
(クエリ実行スレッドで使用される)にPgConnectionNotice
イベント(メインスレッドで実行中)が添付されています。これがこれらのランダムアクセス違反を生成すると思われます。
私はこれをどう対処するか分かりません。私はPgConnectionNotice(Synchronize?)の内部で何らかのlock
を使うべきですか?
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;
(のMsgId = 2)PgConnectionNoticeに送信されたメッセージをOmniEventMonitor1TaskMessage
によって受信されることはない:
これは私が試したものです。
私はCalculationTask.Invoke
を使用しようとしましたが、文字列パラメータを渡すためにそれを呼び出す方法がわかりませんでした(Delphi 2010では匿名関数が許されていないと思います)。私はこのようなクエリをキャンセルする単純な行動をしようとしたとき
、それがクエリをキャンセル停止:
procedure TFDecomptes.DoTheInterrupt;
begin
ThreadConnection.BreakExec;
end;
procedure TFDecomptes.BInterruptClick(Sender: TObject);
begin
CalculationTask.Invoke(DoTheInterrupt);
end;
だから私は、私はCalculationTask
を経由して通話を行うべきではないと思います。 InternalExecQuery
で作成したタスクをグローバル変数に格納して使用する必要がありますか?
通知イベント内で 'GetCurrentThreadId()'をチェックしてください。メイン(UI)スレッドIDと異なる場合は、synchronize()が必要です... – whosrdaddy
ありがとうございます。同期する**。また、私はOTLとSynchronizeを使うことができないと思います。同期はTThreadメソッドです。 –
あなたはTThread.Synchronize()を使用することができます。静的メソッドです.TThreadクラスにする必要はありません。パラメータとして匿名メソッドを使用できます。 – whosrdaddy