System.Threading.TimerからUIを呼び出すときにハンドルのリークを回避する方法は?

StackOverflow https://stackoverflow.com/questions/1603123

質問

System.Threading.TimerからコールバックのwinformsコントロールでInvokeを呼び出すと、タイマーが破棄されるまでハンドルがリークするようです。誰もこれを回避する方法のアイデアを持っていますか? 1秒ごとに値をポーリングし、それに応じて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タスクマネージャーで見ると2ハンドル/秒でリークします。

役に立ちましたか?

解決

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

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

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

他のヒント

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

さて、もう少し時間がかかりましたが、実際にはハンドルがリークしていないように見えますが、これはガベージコレクターの不確定な性質です。ティックごとに最大10ミリ秒でバンプすると、非常に速く上昇し、30秒後には低下しました。

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

興味深い-これは答えではありませんが、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);
    }
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top