8

System.Threading.Timer からのコールバックで winforms コントロールで Invoke を呼び出すと、タイマーが破棄されるまでハンドルがリークするようです。これを回避する方法を知っている人はいますか? 毎秒値をポーリングし、それに応じて UI を更新する必要があります。

テストプロジェクトで試してみて、それが実際にリークの原因であることを確認しました。これは単に次のとおりです。

    System.Threading.Timer timer;
    public Form1()
    {
        InitializeComponent();
        timer = new System.Threading.Timer(new System.Threading.TimerCallback(DoStuff), null, 0, 500);
    }
    void DoStuff(object o)
    {
        this.Invoke(new Action(() => this.Text = "hello world"));
    }

Windows タスク マネージャーで監視すると、1 秒あたり 2 ハンドルがリークします。

4

4 に答える 4

6

Invoke は、メッセージを UI スレッドに投稿し、ハンドルを作成し、そのハンドルを待機して Invoked メソッドがいつ完了するかを判断するという点で、BeginInvoke/EndInvoke のペアのように機能します。「漏れている」のはこのハンドルです。アプリケーションの実行中にProcess Explorerを使用してハンドルを監視すると、名前のないイベントであることがわかります。

IASyncResult が IDisposable の場合、オブジェクトの破棄によってハンドルがクリーンアップされます。そうではないため、ガベージ コレクターが実行され、IASyncResult オブジェクトのファイナライザーが呼び出されると、ハンドルがクリーンアップされます。これは、DoStuff への 20 回の呼び出しごとに GC.Collect() を追加することで確認できます。ハンドル カウントは 20 秒ごとに減少します。もちろん、GC.Collect() への呼び出しを追加して問題を「解決」することは、問題に対処するための間違った方法です。ガベージ コレクターに独自の仕事をさせます。

Invoke 呼び出しを同期する必要がない場合は、Invoke の代わりに BeginInvoke を使用し、EndInvoke を呼び出さないでください。最終結果は同じことを行いますが、ハンドルが作成または「リーク」されることはありません。

于 2009-10-21T20:40:15.203 に答える
2

ここで System.Windows.Forms.Timer を使用できない理由はありますか? タイマーがそのフォームにバインドされている場合は、呼び出す必要さえありません。

于 2009-10-21T19:48:56.290 に答える
2

わかりました、もう少し時間をかけて、実際にはハンドルが漏れていないように見えます。これはガベージ コレクターの不確定な性質です。1 ティックあたり最大 10 ミリ秒まで上げたところ、非常に速く上昇し、30 秒後には元に戻りました。

理論を確認するために、各コールバックで手動で GC.Collect() を呼び出しました (実際のプロジェクトではこれを行わないでください。これはテストのためだけでした。なぜそれが悪い考えなのかについては、数多くの記事があります)。ハンドル数は安定していました。

于 2009-10-21T20:44:55.403 に答える
0

興味深い-これは答えではありませんが、Andreiのコメントに基づいて、これは同じようにハンドルをリークしないと思いましたが、OPが言及したのと同じ速度でハンドルをリークします。

System.Threading.Timer timer;
    public Form2()
    {
        InitializeComponent();

    }

    private void UpdateFormTextCallback()
    {
        this.Text = "Hello World!";
    }

    private Action UpdateFormText;

    private void DoStuff(object value)
    {
        this.Invoke(UpdateFormText);
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        timer = new System.Threading.Timer(new TimerCallback(DoStuff), null, 0, 500);
        UpdateFormText = new Action(UpdateFormTextCallback);
    }
于 2009-10-21T20:00:26.693 に答える