0

長いクエリを別のスレッドで実行して、それらを中止し、ユーザーにフィードバックを提供したいと考えています。これはすべて機能していますが、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、これがランダム アクセス違反を生成していると思われます。

これを処理する方法がわかりません。lockPgConnectionNotice 内で何らかの種類を使用する必要があります (同期しますか?)。

これは私が試したものです:

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グローバル変数に保存して使用する必要がありますか?

4

1 に答える 1

0

主な問題は、私が混乱していたことIOmniTaskIOmniTaskControl. IOmniTaskはメイン スレッドにメッセージを送信するために使用されるバックグラウンドのインターフェイスでありIOmniTaskControl、バックグラウンド タスクと対話するために使用されるメイン スレッドのインターフェイスです。

IOmniTaskControlそのため、内部で CalculationTask ( である ) を使用するPgConnectionNoticeのは二重の間違いです。PgConnectionNoticeはバックグラウンド スレッド内から起動されるため、メイン スレッドの変数を使用して、バックグラウンド スレッドからバックグラウンド スレッドにメッセージを送信していました。

そこで、次の名前のグローバル変数を追加しましたRunningTask

Var RunningTask : IOmniTask;

nilフォームに設定しOnCreate、タスクのコードを次のように変更します。

procedure InternalExecQuery(const task: IOmniTask);
Var q:TPgSQL;
begin
  RunningTask := task;
  try
    q:=Task.Param['pgsql'];
    Try
      q.Execute;
    Except
      On E:Exception do task.Comm.Send(1,e.Message);
    End;
  finally
    RunningTask := Nil;
  end;
end;

OnNoticeイベントは次のようになります。

procedure TFDecomptes.PgConnectionNotice(Sender: TObject; Errors: TPgErrors);
begin
  if RunningTask=Nil then
    // do nothing, old, pending notices
  else
    RunningTask.Comm.Send(2,Errors[Errors.Count-1].ToString);
end;

バックグラウンド タスクが 1 つしかないことはわかっていますが、グローバル変数を定義するのはクリーンではないことはわかっています。これは. IOmniTask_ _ThreadConnectionSenderPgConnectionNotice

于 2016-05-30T18:18:55.460 に答える