3

AsyncCalls を使用している場合、どのようにして例外をメイン スレッドに入れるのが最善でしょうか? 例えば:

procedure TUpdater.needsToGoFirst();
begin
  asyncCall := TAsyncCalls.Invoke(procedure 
  begin 
    //some stuff
    if (possiblyTrueCondition = True) then
      raise Exception.Create('Error Message');
    //more stuff
    TAsyncCalls.VCLSync(procedure 
    begin 
      notifyUpdates(); 
    end);
  end);
end;

procedure TUpdater.needsToGoSecond();
begin
  asyncCall2 := TAsyncCalls.Invoke(procedure 
  begin 
    //initial stuff
    asyncCall.Sync();
    //stuff that needs to go second
    TAsyncCalls.VCLSync(procedure 
    begin 
      notifyUpdates(); 
    end);
  end);
end;

呼び出すasycCall.Syncと例外がスローされることはわかっていますが、現在スレッドに更新が行われたことをメインスレッドに通知させる方法により、メインスレッドで呼び出している場所が実際にはありませんSyncSync最初のスレッドが最初に処理する必要があるリソースを取得する前に、いくつかの設定を確認するために呼び出している別のスレッドが実際にあるため、そうするのも難しいことがわかります。

これらの関数の内部を try-catch でラップし、 a を使用VCLSyncしてメイン スレッドへの例外を自分で取得する必要がありますか? ある種のメインスレッドのアイドルループで例外をチェックするより良い方法はありますか?

編集 1

私が持っていた別の考えは、すべての投稿でそのコードを複製するのではなく、IAscynCall 参照で例外をチェックし、それを使用してメイン スレッドに例外を発生させることだけを行うループを作成することでした。needsToGoSecondメソッドはまだ最初に例外に到達する可能性がありますが、その後例外を保持し、ループはそこで例外をキャッチします。

4

4 に答える 4

1

あなたは次のように述べています:

メインスレッドでSyncと呼んでいる場所は本当にありません。私は実際に同期を呼び出している別のスレッドを持っているので、そうすることも難しいことがわかります。

...。

これらの関数の内部をtry-catchでラップし、VCLSyncを使用してメインスレッドの例外を自分で取得する必要がありますか?ある種のメインスレッドのアイドルループで例外をチェックするためのより良い方法はありますか?

他のスレッドを呼び出すSyncと、そこで例外が発生します。そこで上げたくない場合、およびメインスレッドで上げる必要がある場合は、オプションはありません。非同期プロシージャで、未処理の例外を自分でキャッチする必要があります。次に、呼び出しTThread.Queue、Windowsメッセージの投稿、または同様のキューメカニズムを使用して、それらをメインスレッドにキューイングできます。別のオプションはVCLSync、その時点での同期を気にしない場合に使用することです。

肝心なのは、別のスレッドからの呼び出しとSync、メインスレッドで例外を発生させる必要があることは、互換性のある目標ではないということです。エルゴあなたは自分で例外をキャッチし、それを処理するAsyncCallsを停止する必要があります。

基本的に、これは現在のアプローチを拡張したものにすぎません。現時点では、メインスレッドを同期させるのではなく、メインスレッドに火災通知を送信します。結局のところ、メインスレッドから同期したくないので、非同期アプローチを使用していると思います。したがって、拡大すると、メインスレッドにエラーと例外、およびより正常な結果を通知できるようにする必要があります。

于 2012-09-12T15:47:37.260 に答える
0

リクエストに応じて、スレッド内の例外をメイン スレッドに渡す方法の例を次に示します。例外によりスレッドでの実行が停止し、エラーをトラップしてユーザーに返します。

スレッドで実行するコードを含むワーカー クラスとスレッド クラスの 2 つの非常に単純なクラスを次に示します。

type
  TMyWorker = class
  private
    FExceptionObject: Pointer;
    FExceptionAddress: Pointer;
  public
    constructor Create; reintroduce;
    destructor Destroy; override;
    procedure _Execute;
    procedure GetException;
  end;

implementation

constructor TMyWorker.Create;
begin
  inherited Create;
  FExceptionObject := nil;
  FExceptionAddress := nil;
end;

procedure TMyWorker._Execute;
begin
  try
    // run code
  except
    FExceptionObject := AcquireExceptionObject; // increments exception's refcnt
    FExceptionAddress := ExceptAddr;
  end;
end;

procedure TMyWorker.GetException;
begin
  if Assigned(FExceptionObject) then
    raise Exception(FExceptionObject) at FExceptionAddress; // decrements exception's refcnt
end;

destructor TMyWorker.Destroy;
begin
  if Assigned(FExceptionObject) then
  begin
    ReleaseExceptionObject; // decrements exception's refcnt
    FExceptionObject := nil;
    FExceptionAddress := nil;
  end;
  inherited;
end;

.

type
  TMyThread = class(TThread)
  private
    FWorker: TMyWorker;
  protected
    procedure Execute; override;
  public
    constructor Create(Worker: TMyWorker);
  end;

implementation

procedure TMyThread.Execute;
begin
  FWorker._Execute;
end;

constructor TMyThread.Create(Worker: TMyWorker);
begin
  FWorker := Worker;
  FreeOnTerminate := False;
  inherited Create(False);
end;

次に、メイン スレッド コードでワーカー オブジェクトを作成し、それをスレッドのコンストラクターに渡して実行します。実行が完了すると、メイン スレッドは例外をチェックして再発生させます。

var
  myWorker: TMyWorker;
begin
  myWorker := TMyWorker.Create;
  try
    with TMyThread.Create(myWorker) do
    begin
      WaitFor; // stop execution here until the thread has finished
      Free;    // frees the thread object
    end;
    myWorker.GetException; // re-raise any exceptions that occurred while thread was running
  finally
    FreeAndNil(myWorker);
  end;
end;

次の EDN 記事も参照してください。

于 2012-09-11T23:32:30.773 に答える
0

最終的に、私は自分のニーズに合った次のソリューションを思いつきました。これは、スレッドに関係なく、GUI に例外メッセージを表示させたいだけです。エラーをより具体的に管理したい場合は、Davidの回答をお勧めします。

まず、使い方asyncCall.Sync();が間違っていました。TEvent問題の実際のイベントが発生するのを待つためにオブジェクトを使用しています。スレッドは、待機中のスレッドを必要以上に長く待たせることなく、他の作業を続行できます。

次に、ループを使用して発生した例外をキャッチし、エラーをメイン スレッドに同期しています。たとえば、1 つのスレッドは次のようになります。

procedure TUpdater.needsToGoSecond();
begin
  fAsyncThreads.Add(TAsyncCalls.Invoke(procedure 
  begin 
    //initial stuff
    myEvent.Wait();
    //stuff that needs to go second
    if (possiblyTrueCondition = True) then
      raise Exception.Create('Error Message');
    TAsyncCalls.VCLSync(procedure 
    begin 
      notifyUpdates(); 
    end);
  end));
end;

そして、別のスレッドで例外をキャッチして発生させています。

procedure TUpdater.catchExceptions();
begin
  fAsyncCatchExceptions := TAsyncCalls.Invoke(procedure
  var
    asyncThread: IAsyncCall;
    errorText: string;
  begin
    while(true)do
    begin
      for asyncThread in fAsyncThreads do
      begin
        if(Assigned(asyncThread)) and (asyncThread.Finished)then
        begin
          try
            asyncThread.Sync();
          except on E: Exception do
            begin
              errorText := E.Message;
              TAsyncCalls.VCLInvoke(procedure begin raise Exception.Create(errorText); end);
            end;
          end;
          fAsyncThreads.Remove(asyncThread);
        end;//if
      end;//for
      Sleep(2000);
    end;//while
  end);
end;

例外は、VCLSync (または TThread.Synchronize) 呼び出しではなく、VCLInvoke (または TThread.Queue) 呼び出しによってスローされる必要があるようです。同期するとき、外側の AsyncCall が例外をキャッチし、GUI に表示されないようにしていると思います。また、私の catch ループは、メイン スレッドで発生させる例外のコピーをまだ作成していません。raise コマンドをキューに入れる必要があるように見えるため、現在の例外を再発生させることはできません。アクセス違反が発生するのは、GUI が到達するまでにクリーンアップされる可能性が最も高いためです。

于 2012-09-28T18:07:46.913 に答える
-1

さて、私たちは不正確な答えになったので、ここにもう1つあります.

OmniThreadLibrary: http://otl.17slon.com/

フォーラム (そして著者はそこで非常に反応が良い): http://otl.17slon.com/forum/

非同期の例: http://www.thedelphigeek.com/2012/07/asyncawait-in-delphi.html

本のサンプル: http://samples.leanpub.com/omnithreadlibrary-sample.pdf

  • セクション 2.1 で Async を紹介します。
  • セクション 2.1.1 は、Async での例外処理を正確にカバーしています。

OTL の例外の詳細: http://www.thedelphigeek.com/2011/07/life-after-21-exceptions-in.html

  • つまり、非同期を使用せず、スレッド化プリミティブは統一されているため、非同期にも適用する必要があります
于 2012-09-12T09:29:11.693 に答える