1

GridViewといくつかのユーティリティボタンを保持するタイプのコントロールがあります。コントロールは、私のアプリケーションのどこでも使用されます。デリゲートを介して、非同期で入力されます。

    protected virtual void PopulateGridView()
    {
        if (isPopulating) return;

        //a delegate given to the control by its parent form
        if (GetterMethod != null)
        {
            isPopulating = true;
            /*unimportant UI fluff here*/

            //some controls are fast enough to not have to mess with threading
            if(PopulateSynchronously) 
            {
                PopulateWithGetterMethod();
                InitializeGridView();
            }
            else //most aren't
            {
                Action asyncMethod = PopulateWithGetterMethod;
                asyncMethod.BeginInvoke(
                   ar => Invoke((MethodInvoker)InitializeGridView)), null);
            }
        }
    }

    private void PopulateWithGetterMethod()
    {
        //a list of whetever the control is displaying;
        //the control ancestor and this collection are generic.
        RetrievedInformation = GetterMethod();
    }

    protected virtual void InitializeGridView()
    {
        //use RetrievedInformation to repopulate the GridView;
        //implementation not important, except it touches UI elements,
        //so it needs to be called from the worker thread using Invoke.
    }

実行時間の長いクエリでは、ユーザーが焦ってウィンドウを閉じることがありました。または、コントロールの1つがタイマーに基づいて自動更新されている場合、ユーザーは偶然にウィンドウを閉じます。それが発生し、クエリDIDが終了すると、コントロールにウィンドウハンドルがなかったため、コールバックデリゲートのInvoke呼び出しはInvalidOperationExceptionで失敗しました。

これを修正するために、組み込みのIsHandleCreatedプロパティを使用しようとしました。

    ...
            else
            {
                Action asyncMethod = PopulateWithGetterMethod;
                asyncMethod.BeginInvoke(
                   ar => { if(IsHandleCreated)
                              Invoke((MethodInvoker)InitializeGridView)); 
                         }, null);
            }

ただし、例外はまだ発生しますが、それほど頻繁ではありません。私はそれをなんとか再現し、IsHandleCreatedのウォッチがfalseを示していても、Invoke呼び出しがまだ発生していることを発見しました。私の推測では、スレッドは、イベントデリゲートを発生させる前にnullをチェックする場合に見られるように、チェックとInvoke呼び出しの間に横取りされたと思います。

私にはまだ選択肢があると思いますが、何が最善か疑問に思っています。

  • IsHandleCreatedだけでなく、Disposedもチェックして、コントロールが実際に正常に機能していることを確認し、破壊されようとしているだけではないことを確認します。
  • チェックを行う前にThread.Yield()を実行して、OSがハンドルをチェックする前にウィンドウ管理を実行できるようにします。
  • Invoke呼び出しを、InvalidOperationExceptions、またはウィンドウハンドルの欠如を報告する少なくとも1つを抑制するtry/catchでラップします。正直なところ、この場合、GridViewを更新できないことは気にしません。ユーザーがウィンドウを閉じたので、明らかに彼らは気にしません。アプリ全体を停止することなく、スレッドを静かに終了させます。

3番目のオプションはコップアウトのようです。それを処理するためのよりクリーンな方法が必要です。しかし、他の2つのいずれかが100%修正されるかどうかはわかりません。

編集:DisposedとIsDisposedのチェックも機能しませんでした。条件が「IsHandleCreated&&!Disposed &&!IsDisposed」のifブロック内から例外がスローされました。この例外では、監視時に最初と最後のノードがfalseでした。現在、「ウィンドウハンドルが作成されるまで、コントロールでInvokeまたはBeginInvokeを呼び出すことはできません。」というメッセージを表示して、すべての例外をトラップしています。これは、実行したくないことです。

4

2 に答える 2

4

はい、これを行うには100%クリーンな方法があります。フォームを閉じる前にスレッドを終了します。それ以外は厄介なカットに対するバンドエイドであり、フォームがまだ生きているかどうかをチェックすることは、解決できない不可避の競合状態です。Invoke()を呼び出すと、フォームがgonzoである可能性を最小限に抑えることができるだけであり、それらを排除することはできません。

パターンについては、この回答を確認してください。

于 2011-04-21T16:58:57.123 に答える
1

処分はあなたの最善の策です。ただし、この同じ問題がときどき発生し、Disposed呼び出しがfalseを返すことがありますが、3行後であっても、破棄されたコントロールを使用しようとするときまでに。

このような場合、特定の例外をキャッチし、例外テキストに探しているものが含まれていることを確認するのが最善であることがわかりました。それが例外であり、既知の例外である場合、それを飲み込むことはひどい考えではありません。重要なのは、探している特定の例外のみを飲み込んでいることを確認することです。

于 2011-04-21T16:54:45.373 に答える