3

Formsウィンドウをダイアログボックスとして表示しています

private void buttonOverview_Click(object sender, EventArgs e)
{
    (new OverviewBox()).ShowDialog();
    MessageBox.Show("Window Exited");
}

OverviewBoxコンストラクター内でインスタンス化されるリフレッシュ タイマーがある

public OverviewBox()
{
    InitializeComponent();

    this._polltimer = new Timer { Interval = 30000, Enabled = true };
    this._polltimer.Tick += (sender, e) => { this.Poll(); };
}

メソッドPollは非同期的にデータベースからデータを取得し、ビューをフリーズせずに更新します。

private void Poll()
{
    Task.Run(() =>
    {
        if (!SessionContext.Connectable())
        {
            return;
        }
        try
        {
            [logics to get data]
            this.dgvChangeCoordinators.BeginInvoke(new Action(() => { SetDataGridView(this.dataGridView, "<Data Description>", listwithdata); }));
        }
        catch (Exception ex)
        {
            Logger.Log(ex.ToString());
            throw;
        }
    });
}

SetDataGridViewitemsourceの時点でリストを設定しdatagridview、データの説明を表示します。ただし、ユーザーが例外について不満を言うことがあります。例外ログは次のようになります。

7/15/2013 5:00:10 PM:
System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.BeginInvoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.BeginInvoke(Delegate method)
at FormulierGenerator.Views.Agent.OverviewBox.<Poll>b__5()


7/15/2013 5:00:23 PM:
System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.BeginInvoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.BeginInvoke(Delegate method)
at FormulierGenerator.Views.Agent.OverviewBox.<Poll>b__5()


7/15/2013 5:00:40 PM:
System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.BeginInvoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.BeginInvoke(Delegate method)
at FormulierGenerator.Views.Agent.OverviewBox.<Poll>b__5()


7/15/2013 5:00:53 PM:
System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.BeginInvoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.BeginInvoke(Delegate method)
at FormulierGenerator.Views.Agent.OverviewBox.<Poll>b__5()

例外間の時間差から、タイマーの少なくとも 2 つのインスタンスがまだアクティブであると結論付けます (ポーリング間の 30 秒、2 つのグループが 30 秒以内にある 4 つの異なる時間、ポーリング間隔)。ただし、概要を 2 回開始して閉じるだけでは、問題をシミュレートすることはできません。

GCウィンドウ オブジェクトがある時点で収集されるが、ポーラーが存在し続けるという関連する問題が疑われます。ウィンドウ スレッド コンテキストでウィンドウを更新しようとすると、失敗します。しかし、 Window オブジェクトとそのすべてのコンテンツは、 private void のコンテキストにのみ存在するべきではありませんbuttonOverview_Clickか? ボタン メソッドに MessageBox.Show() 呼び出しを追加して、ダイアログを閉じた後にメソッドが完了したかどうかをテストしました。それは示しています。

メソッドにブレークポイントを設定してPoll、ダイアログが閉じられた後もメソッドが呼び出されているかどうかを確認します。そうだったので、ポーラーは間違いなくウィンドウが表示されているよりも長く生きています。私の質問は、これまでのところ私の結論は正しいですか? もしそうなら、タイマーを作成するオブジェクトがインスタンス化されたコンテキストが存在しなくなったとしても、ポーラーはどのように存在し続けることができますか? アンロード イベント アクションを考えていますが、それが最善の解決策かどうかはわかりません。

4

2 に答える 2

5

まず第一に、ガベージ コレクターは決定論的ではありません。ウィンドウが閉じられていてどこにも参照されていない場合でも、ウィンドウが実際に収集されるまでに長い時間がかかる可能性があります。Tickイベントの登録を解除し、ウィンドウが閉じたらすぐに設定IsEnabledする必要があります。false

そうは言っても、ここでの本当の問題はSystem.Windows.Forms.Timerそれ自体です。有効になるとすぐに、GCHandle自分自身に を割り当て、ガベージ コレクションを防ぎます。次に、イベント ハンドラーはウィンドウが収集されるのを防ぎます。これは通常の場合であり、発生していると思われる方法ではありません。

System.Windows.Forms.Timerは、破棄されるとそれ自体を無効にして、この問題を防ぎFormます。また、フォームを閉じると、a のすべてのコンポーネントが自動的に破棄されることに注意してください。Timerただし、フォーム コンポーネントとして登録していないため、Dispose自動的に呼び出されることはありません。Timerツールボックスを使用してフォームに を追加するか、 を使用new Timer(components)してインスタンス化して、問題が消えることを確認する必要があります。

于 2013-07-24T16:30:55.037 に答える